aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSpace Walker <[email protected]>2022-06-14 11:41:05 +0200
committerGitHub <[email protected]>2022-06-14 11:41:05 +0200
commitef5d95f9fbb38cfa03d64188f6fab13a15598c93 (patch)
tree30c61c93189406a36c001b89823a4a2df34c8a54
parent3eaf3a8f5e7ecd5d797ecc33445bd31c7cba6047 (diff)
downloadPaper-ef5d95f9fbb38cfa03d64188f6fab13a15598c93.tar.gz
Paper-ef5d95f9fbb38cfa03d64188f6fab13a15598c93.zip
Update alternate current patch (#7971)
-rw-r--r--patches/server/0904-Add-Alternate-Current-redstone-implementation.patch789
1 files changed, 435 insertions, 354 deletions
diff --git a/patches/server/0904-Add-Alternate-Current-redstone-implementation.patch b/patches/server/0904-Add-Alternate-Current-redstone-implementation.patch
index 9b1ff6ca31..7a58aa6ed5 100644
--- a/patches/server/0904-Add-Alternate-Current-redstone-implementation.patch
+++ b/patches/server/0904-Add-Alternate-Current-redstone-implementation.patch
@@ -28,12 +28,14 @@ switch between the vanilla, Eigencraft, and Alternate Current implementations.
diff --git a/src/main/java/alternate/current/wire/LevelHelper.java b/src/main/java/alternate/current/wire/LevelHelper.java
new file mode 100644
-index 0000000000000000000000000000000000000000..186e0a86ceaed007d394378157ce1596a0601908
+index 0000000000000000000000000000000000000000..f55c5c67b8461e9ef5614ea1a37f6e2866f39be3
--- /dev/null
+++ b/src/main/java/alternate/current/wire/LevelHelper.java
-@@ -0,0 +1,64 @@
+@@ -0,0 +1,65 @@
+package alternate.current.wire;
+
++import org.bukkit.event.block.BlockRedstoneEvent;
++
+import net.minecraft.core.BlockPos;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.block.Block;
@@ -42,8 +44,6 @@ index 0000000000000000000000000000000000000000..186e0a86ceaed007d394378157ce1596
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.LevelChunkSection;
+
-+import org.bukkit.event.block.BlockRedstoneEvent;
-+
+public class LevelHelper {
+
+ static int doRedstoneEvent(ServerLevel level, BlockPos pos, int prevPower, int newPower) {
@@ -54,9 +54,10 @@ index 0000000000000000000000000000000000000000..186e0a86ceaed007d394378157ce1596
+ }
+
+ /**
-+ * An optimized version of Level.setBlock. Since this method is only used to
-+ * update redstone wire block states, lighting checks, height map updates, and
-+ * block entity updates are omitted.
++ * An optimized version of {@link net.minecraft.world.level.Level#setBlock
++ * Level.setBlock}. Since this method is only used to update redstone wire block
++ * states, lighting checks, height map updates, and block entity updates are
++ * omitted.
+ */
+ static boolean setWireState(ServerLevel level, BlockPos pos, BlockState state, boolean updateNeighborShapes) {
+ int y = pos.getY();
@@ -98,10 +99,10 @@ index 0000000000000000000000000000000000000000..186e0a86ceaed007d394378157ce1596
+}
diff --git a/src/main/java/alternate/current/wire/Node.java b/src/main/java/alternate/current/wire/Node.java
new file mode 100644
-index 0000000000000000000000000000000000000000..acfe97d62ef863a14e2e43db30917bd57bbd5171
+index 0000000000000000000000000000000000000000..3e25047fd8db65bf34bb39ae9a07fe01b5b61786
--- /dev/null
+++ b/src/main/java/alternate/current/wire/Node.java
-@@ -0,0 +1,97 @@
+@@ -0,0 +1,113 @@
+package alternate.current.wire;
+
+import java.util.Arrays;
@@ -134,6 +135,15 @@ index 0000000000000000000000000000000000000000..acfe97d62ef863a14e2e43db30917bd5
+
+ private int flags;
+
++ /** The previous node in the update queue. */
++ Node prev;
++ /** The next node in the update queue. */
++ Node next;
++ /** The priority with which this node was queued. */
++ int priority;
++ /** The wire that queued this node for an update. */
++ WireNode neighborWire;
++
+ Node(ServerLevel level) {
+ this.level = level;
+ this.neighbors = new Node[Directions.ALL.length];
@@ -183,6 +193,13 @@ index 0000000000000000000000000000000000000000..acfe97d62ef863a14e2e43db30917bd5
+ return this;
+ }
+
++ /**
++ * Determine the priority with which this node should be queued.
++ */
++ int priority() {
++ return neighborWire.priority;
++ }
++
+ public boolean isWire() {
+ return false;
+ }
@@ -199,12 +216,12 @@ index 0000000000000000000000000000000000000000..acfe97d62ef863a14e2e43db30917bd5
+ throw new UnsupportedOperationException("Not a WireNode!");
+ }
+}
-diff --git a/src/main/java/alternate/current/wire/PowerQueue.java b/src/main/java/alternate/current/wire/PowerQueue.java
+diff --git a/src/main/java/alternate/current/wire/UpdateQueue.java b/src/main/java/alternate/current/wire/UpdateQueue.java
new file mode 100644
-index 0000000000000000000000000000000000000000..9c293bef09ad1c95ba24ea6c41a27c5489b548dd
+index 0000000000000000000000000000000000000000..3afe0ef9dfe16c2dc144b553069286b6e72eac1b
--- /dev/null
-+++ b/src/main/java/alternate/current/wire/PowerQueue.java
-@@ -0,0 +1,209 @@
++++ b/src/main/java/alternate/current/wire/UpdateQueue.java
+@@ -0,0 +1,211 @@
+package alternate.current.wire;
+
+import java.util.AbstractQueue;
@@ -213,99 +230,99 @@ index 0000000000000000000000000000000000000000..9c293bef09ad1c95ba24ea6c41a27c54
+
+import net.minecraft.world.level.redstone.Redstone;
+
-+public class PowerQueue extends AbstractQueue<WireNode> {
++public class UpdateQueue extends AbstractQueue<Node> {
+
+ private static final int OFFSET = -Redstone.SIGNAL_MIN;
+
-+ /** The last wire for each power level. */
-+ private final WireNode[] tails;
++ /** The last node for each priority value. */
++ private final Node[] tails;
+
-+ private WireNode head;
-+ private WireNode tail;
++ private Node head;
++ private Node tail;
+
+ private int size;
+
-+ public PowerQueue() {
-+ this.tails = new WireNode[(Redstone.SIGNAL_MAX + 1) - Redstone.SIGNAL_MIN];
++ public UpdateQueue() {
++ this.tails = new Node[(Redstone.SIGNAL_MAX + OFFSET) + 1];
+ }
+
+ @Override
-+ public boolean offer(WireNode wire) {
-+ if (wire == null) {
++ public boolean offer(Node node) {
++ if (node == null) {
+ throw new NullPointerException();
+ }
+
-+ int power = wire.nextPower();
++ int priority = node.priority();
+
-+ if (contains(wire)) {
-+ if (wire.power == power) {
-+ // already queued for this power; exit
++ if (contains(node)) {
++ if (node.priority == priority) {
++ // already queued with this priority; exit
+ return false;
+ } else {
-+ // already queued for different power; move it
-+ move(wire, power);
++ // already queued with different priority; move it
++ move(node, priority);
+ }
+ } else {
-+ insert(wire, power);
++ insert(node, priority);
+ }
+
+ return true;
+ }
+
+ @Override
-+ public WireNode poll() {
++ public Node poll() {
+ if (head == null) {
+ return null;
+ }
+
-+ WireNode wire = head;
-+ WireNode next = wire.next;
++ Node node = head;
++ Node next = node.next;
+
+ if (next == null) {
+ clear(); // reset the tails array
+ } else {
-+ if (wire.power != next.power) {
-+ // if the head is also a tail, its entry in the array
-+ // can be cleared; there is no previous wire with the
-+ // same power to take its place.
-+ tails[wire.power + OFFSET] = null;
++ if (node.priority != next.priority) {
++ // If the head is also a tail, its entry in the array
++ // can be cleared; there is no previous node with the
++ // same priority to take its place.
++ tails[node.priority + OFFSET] = null;
+ }
+
-+ wire.next = null;
++ node.next = null;
+ next.prev = null;
+ head = next;
+
+ size--;
+ }
+
-+ return wire;
++ return node;
+ }
+
+ @Override
-+ public WireNode peek() {
++ public Node peek() {
+ return head;
+ }
+
+ @Override
+ public void clear() {
-+ for (WireNode wire = head; wire != null; ) {
-+ WireNode w = wire;
-+ wire = wire.next;
++ for (Node node = head; node != null; ) {
++ Node n = node;
++ node = node.next;
+
-+ w.prev = null;
-+ w.next = null;
++ n.prev = null;
++ n.next = null;
+ }
+
++ Arrays.fill(tails, null);
++
+ head = null;
+ tail = null;
+
-+ Arrays.fill(tails, null);
-+
+ size = 0;
+ }
+
+ @Override
-+ public Iterator<WireNode> iterator() {
++ public Iterator<Node> iterator() {
+ throw new UnsupportedOperationException();
+ }
+
@@ -314,96 +331,98 @@ index 0000000000000000000000000000000000000000..9c293bef09ad1c95ba24ea6c41a27c54
+ return size;
+ }
+
-+ public boolean contains(WireNode wire) {
-+ return wire == head || wire.prev != null;
++ public boolean contains(Node node) {
++ return node == head || node.prev != null;
+ }
+
-+ private void move(WireNode wire, int power) {
-+ remove(wire);
-+ insert(wire, power);
++ private void move(Node node, int priority) {
++ remove(node);
++ insert(node, priority);
+ }
+
-+ private void remove(WireNode wire) {
-+ if (wire == tail || wire.power != wire.next.power) {
-+ // assign a new tail for this wire's power
-+ if (wire == head || wire.power != wire.prev.power) {
-+ // there is no other wire with the same power; clear
-+ tails[wire.power + OFFSET] = null;
++ private void remove(Node node) {
++ Node prev = node.prev;
++ Node next = node.next;
++
++ if (node == tail || node.priority != next.priority) {
++ // assign a new tail for this node's priority
++ if (node == head || node.priority != prev.priority) {
++ // there is no other node with the same priority; clear
++ tails[node.priority + OFFSET] = null;
+ } else {
-+ // the previous wire in the queue becomes the tail
-+ tails[wire.power + OFFSET] = wire.prev;
++ // the previous node in the queue becomes the tail
++ tails[node.priority + OFFSET] = prev;
+ }
+ }
+
-+ if (wire == head) {
-+ head = wire.next;
++ if (node == head) {
++ head = next;
+ } else {
-+ wire.prev.next = wire.next;
++ prev.next = next;
+ }
-+ if (wire == tail) {
-+ tail = wire.prev;
++ if (node == tail) {
++ tail = prev;
+ } else {
-+ wire.next.prev = wire.prev;
++ next.prev = prev;
+ }
+
-+ wire.prev = null;
-+ wire.next = null;
++ node.prev = null;
++ node.next = null;
+
+ size--;
+ }
+
-+ private void insert(WireNode wire, int power) {
-+ // store the power for which this wire is queued
-+ wire.power = power;
++ private void insert(Node node, int priority) {
++ node.priority = priority;
+
-+ // wires are sorted by power (highest to lowest)
-+ // wires with the same power are ordered FIFO
++ // nodes are sorted by priority (highest to lowest)
++ // nodes with the same priority are ordered FIFO
+ if (head == null) {
+ // first element in this queue \o/
-+ head = tail = wire;
-+ } else if (wire.power > head.power) {
-+ linkHead(wire);
-+ } else if (wire.power <= tail.power) {
-+ linkTail(wire);
++ head = tail = node;
++ } else if (priority > head.priority) {
++ linkHead(node);
++ } else if (priority <= tail.priority) {
++ linkTail(node);
+ } else {
-+ // since the wire is neither the head nor the tail
++ // since the node is neither the head nor the tail
+ // findPrev is guaranteed to find a non-null element
-+ linkAfter(findPrev(wire), wire);
++ linkAfter(findPrev(node), node);
+ }
+
-+ tails[power + OFFSET] = wire;
++ tails[priority + OFFSET] = node;
+
+ size++;
+ }
+
-+ private void linkHead(WireNode wire) {
-+ wire.next = head;
-+ head.prev = wire;
-+ head = wire;
++ private void linkHead(Node node) {
++ node.next = head;
++ head.prev = node;
++ head = node;
+ }
+
-+ private void linkTail(WireNode wire) {
-+ tail.next = wire;
-+ wire.prev = tail;
-+ tail = wire;
++ private void linkTail(Node node) {
++ tail.next = node;
++ node.prev = tail;
++ tail = node;
+ }
+
-+ private void linkAfter(WireNode prev, WireNode wire) {
-+ linkBetween(prev, wire, prev.next);
++ private void linkAfter(Node prev, Node node) {
++ linkBetween(prev, node, prev.next);
+ }
+
-+ private void linkBetween(WireNode prev, WireNode wire, WireNode next) {
-+ prev.next = wire;
-+ wire.prev = prev;
++ private void linkBetween(Node prev, Node node, Node next) {
++ prev.next = node;
++ node.prev = prev;
+
-+ wire.next = next;
-+ next.prev = wire;
++ node.next = next;
++ next.prev = node;
+ }
+
-+ private WireNode findPrev(WireNode wire) {
-+ WireNode prev = null;
++ private Node findPrev(Node node) {
++ Node prev = null;
+
-+ for (int i = wire.power + OFFSET; i < tails.length; i++) {
++ for (int i = node.priority + OFFSET; i < tails.length; i++) {
+ prev = tails[i];
+
+ if (prev != null) {
@@ -452,10 +471,10 @@ index 0000000000000000000000000000000000000000..4fd8cb29024330397cfe4cbc1f237d28
+}
diff --git a/src/main/java/alternate/current/wire/WireConnectionManager.java b/src/main/java/alternate/current/wire/WireConnectionManager.java
new file mode 100644
-index 0000000000000000000000000000000000000000..1dae7c0b896300ac9f0dd360dca23ca1de24dc66
+index 0000000000000000000000000000000000000000..5a7209f05b549c222f6c9bc2af2a35790964947e
--- /dev/null
+++ b/src/main/java/alternate/current/wire/WireConnectionManager.java
-@@ -0,0 +1,139 @@
+@@ -0,0 +1,136 @@
+package alternate.current.wire;
+
+import java.util.Arrays;
@@ -562,19 +581,17 @@ index 0000000000000000000000000000000000000000..1dae7c0b896300ac9f0dd360dca23ca1
+ tail = connection;
+ }
+
++ total++;
++
+ if (heads[connection.iDir] == null) {
+ heads[connection.iDir] = connection;
-+
+ flowTotal |= (1 << connection.iDir);
+ }
-+
-+ total++;
+ }
+
-+ /**
-+ * Iterate over all connections, without any specific
-+ * update order in mind. Use this method if the iteration
-+ * order is not important.
++ /**
++ * Iterate over all connections. Use this method if the iteration order is not
++ * important.
+ */
+ void forEach(Consumer<WireConnection> consumer) {
+ for (WireConnection c = head; c != null; c = c.next) {
@@ -583,9 +600,8 @@ index 0000000000000000000000000000000000000000..1dae7c0b896300ac9f0dd360dca23ca1
+ }
+
+ /**
-+ * Iterate over all connections in an order determined by
-+ * the given flow direction. Use this method if the iteration
-+ * order is important.
++ * Iterate over all connections. Use this method if the iteration order is
++ * important.
+ */
+ void forEach(Consumer<WireConnection> consumer, int iFlowDir) {
+ for (int iDir : WireHandler.CARDINAL_UPDATE_ORDERS[iFlowDir]) {
@@ -597,16 +613,17 @@ index 0000000000000000000000000000000000000000..1dae7c0b896300ac9f0dd360dca23ca1
+}
diff --git a/src/main/java/alternate/current/wire/WireHandler.java b/src/main/java/alternate/current/wire/WireHandler.java
new file mode 100644
-index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f31b8f4e2f
+index 0000000000000000000000000000000000000000..bdc4969e157de034929c8a0ffebc62075aad59f8
--- /dev/null
+++ b/src/main/java/alternate/current/wire/WireHandler.java
-@@ -0,0 +1,1106 @@
+@@ -0,0 +1,1179 @@
+package alternate.current.wire;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Queue;
++import java.util.function.Consumer;
+
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
@@ -853,6 +870,10 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ Directions.UP
+ };
+
++ private static final int POWER_MAX = Redstone.SIGNAL_MAX;
++ private static final int POWER_MIN = Redstone.SIGNAL_MIN;
++ private static final int POWER_STEP = 1;
++
+ // If Vanilla will ever multi-thread the ticking of levels, there should
+ // be only one WireHandler per level, in case redstone updates in multiple
+ // levels at the same time. There are already mods that add multi-threading
@@ -863,8 +884,8 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ private final List<WireNode> network;
+ /** Map of wires and neighboring blocks. */
+ private final Long2ObjectMap<Node> nodes;
-+ /** All the power changes that need to happen. */
-+ private final Queue<WireNode> powerChanges;
++ /** The queue for updating wires and neighboring nodes. */
++ private final Queue<Node> updates;
+
+ private int rootCount;
+ // Rather than creating new nodes every time a network is updated we keep
@@ -872,19 +893,24 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ private Node[] nodeCache;
+ private int nodeCount;
+
-+ private boolean updatingPower;
++ /** Is this WireHandler currently working through the update queue? */
++ private boolean updating;
+
+ public WireHandler(ServerLevel level) {
+ this.level = level;
+
+ this.network = new ArrayList<>();
+ this.nodes = new Long2ObjectOpenHashMap<>();
-+ this.powerChanges = new PowerQueue();
++ this.updates = new UpdateQueue();
+
+ this.nodeCache = new Node[16];
+ this.fillNodeCache(0, 16);
+ }
+
++ /**
++ * Retrieve the {@link alternate.current.wire.Node Node} that represents the
++ * block at the given position in the level.
++ */
+ private Node getOrAddNode(BlockPos pos) {
+ return nodes.compute(pos.asLong(), (key, node) -> {
+ if (node == null) {
@@ -901,46 +927,83 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ }
+
+ /**
-+ * Retrieve the neighbor of a node in the given direction and create a link
-+ * between the two nodes.
++ * Return a {@link alternate.current.wire.Node Node} that represents the block
++ * at the given position.
+ */
-+ private Node getNeighbor(Node node, int iDir) {
-+ Node neighbor = node.neighbors[iDir];
++ private Node getNextNode(BlockPos pos) {
++ return getNextNode(pos, level.getBlockState(pos));
++ }
+
-+ if (neighbor == null || neighbor.invalid) {
-+ Direction dir = Directions.ALL[iDir];
-+ BlockPos pos = node.pos.relative(dir);
++ /**
++ * Return a node that represents the given position and block state. If it is a
++ * wire, then create a new {@link alternate.current.wire.WireNode WireNode}.
++ * Otherwise, grab the next {@link alternate.current.wire.Node Node} from the
++ * cache and update it.
++ */
++ private Node getNextNode(BlockPos pos, BlockState state) {
++ return state.is(Blocks.REDSTONE_WIRE) ? new WireNode(level, pos, state) : getNextNode().update(pos, state, true);
++ }
+
-+ Node oldNeighbor = neighbor;
-+ neighbor = getOrAddNode(pos);
++ /**
++ * Grab the first unused Node from the cache. If all of the cache is already in
++ * use, increase it in size first.
++ */
++ private Node getNextNode() {
++ if (nodeCount == nodeCache.length) {
++ increaseNodeCache();
++ }
+
-+ if (neighbor != oldNeighbor) {
-+ int iOpp = Directions.iOpposite(iDir);
++ return nodeCache[nodeCount++];
++ }
+
-+ node.neighbors[iDir] = neighbor;
-+ neighbor.neighbors[iOpp] = node;
-+ }
++ private void increaseNodeCache() {
++ Node[] oldCache = nodeCache;
++ nodeCache = new Node[oldCache.length << 1];
++
++ for (int index = 0; index < oldCache.length; index++) {
++ nodeCache[index] = oldCache[index];
+ }
+
-+ return neighbor;
++ fillNodeCache(oldCache.length, nodeCache.length);
++ }
++
++ private void fillNodeCache(int start, int end) {
++ for (int index = start; index < end; index++) {
++ nodeCache[index] = new Node(level);
++ }
+ }
+
+ private Node removeNode(BlockPos pos) {
+ return nodes.remove(pos.asLong());
+ }
+
++ /**
++ * Try to revalidate the given node by looking at the block state that is
++ * occupying its position. If the given node is a wire but the block state is
++ * not, a new node must be created/grabbed from the cache. Otherwise, the node
++ * can be quickly revalidated with the new block state;
++ */
+ private Node revalidateNode(Node node) {
++ BlockPos pos = node.pos;
++ BlockState state = level.getBlockState(pos);
++
++ boolean wasWire = node.isWire();
++ boolean isWire = state.is(Blocks.REDSTONE_WIRE);
++
++ if (wasWire != isWire) {
++ return getNextNode(pos, state);
++ }
++
+ node.invalid = false;
+
-+ if (node.isWire()) {
++ if (isWire) {
++ // No need to update the block state of this wire - it will grab
++ // the current block state just before setting power anyway.
+ WireNode wire = node.asWire();
+
+ wire.prepared = false;
+ wire.inNetwork = false;
+ } else {
-+ BlockPos pos = node.pos;
-+ BlockState state = level.getBlockState(pos);
-+
+ node.update(pos, state, false);
+ }
+
@@ -948,47 +1011,102 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ }
+
+ /**
-+ * Check the BlockState that occupies the given position. If it is a wire, then
-+ * create a new WireNode. Otherwise, grab the next Node from the cache and
-+ * update it.
++ * Retrieve the neighbor of a node in the given direction and create a link
++ * between the two nodes if they are not yet linked.
+ */
-+ private Node getNextNode(BlockPos pos) {
-+ BlockState state = level.getBlockState(pos);
++ private Node getNeighbor(Node node, int iDir) {
++ Node neighbor = node.neighbors[iDir];
+
-+ if (state.is(Blocks.REDSTONE_WIRE)) {
-+ return new WireNode(level, pos, state);
-+ }
++ if (neighbor == null || neighbor.invalid) {
++ Direction dir = Directions.ALL[iDir];
++ BlockPos pos = node.pos.relative(dir);
+
-+ return getNextNode().update(pos, state, true);
-+ }
++ Node oldNeighbor = neighbor;
++ neighbor = getOrAddNode(pos);
+
-+ /**
-+ * Grab the first unused Node from the cache. If all of the cache is already in
-+ * use, increase it in size first.
-+ */
-+ private Node getNextNode() {
-+ if (nodeCount == nodeCache.length) {
-+ increaseNodeCache();
++ if (neighbor != oldNeighbor) {
++ int iOpp = Directions.iOpposite(iDir);
++
++ node.neighbors[iDir] = neighbor;
++ neighbor.neighbors[iOpp] = node;
++ }
+ }
+
-+ return nodeCache[nodeCount++];
++ return neighbor;
+ }
+
-+ private void increaseNodeCache() {
-+ Node[] oldCache = nodeCache;
-+ nodeCache = new Node[oldCache.length << 1];
++ /**
++ * Iterate over all neighboring nodes of the given wire. The iteration order is
++ * designed to be an extension of the default block update order, and is
++ * determined as follows:
++ * <br>
++ * 1. The direction of power flow through the wire is to be considered
++ * 'forward'. The iteration order depends on the neighbors' relative positions
++ * to the wire.
++ * <br>
++ * 2. Each neighbor is identified by the step(s) you must take, starting at the
++ * wire, to reach it. Each step is 1 block, thus the position of a neighbor is
++ * encoded by the direction(s) of the step(s), e.g. (right), (down), (up, left),
++ * etc.
++ * <br>
++ * 3. Neighbors are iterated over in pairs that lie on opposite sides of the
++ * wire.
++ * <br>
++ * 4. Neighbors are iterated over in order of their distance from the wire. This
++ * means they are iterated over in 3 groups: direct neighbors first, then
++ * diagonal neighbors, and last are the far neighbors that are 2 blocks directly
++ * out.
++ * <br>
++ * 5. The order within each group is determined using the following basic order:
++ * { front, back, right, left, down, up }. This order was chosen because it
++ * converts to the following order of absolute directions when west is said to
++ * be 'forward': { west, east, north, south, down, up } - this is the order of
++ * shape updates.
++ */
++ private void forEachNeighbor(WireNode wire, Consumer<Node> consumer) {
++ int forward = wire.iFlowDir;
++ int rightward = (forward + 1) & 0b11;
++ int backward = (forward + 2) & 0b11;
++ int leftward = (forward + 3) & 0b11;
++ int downward = Directions.DOWN;
++ int upward = Directions.UP;
++
++ Node front = getNeighbor(wire, forward);
++ Node right = getNeighbor(wire, rightward);
++ Node back = getNeighbor(wire, backward);
++ Node left = getNeighbor(wire, leftward);
++ Node below = getNeighbor(wire, downward);
++ Node above = getNeighbor(wire, upward);
+
-+ for (int index = 0; index < oldCache.length; index++) {
-+ nodeCache[index] = oldCache[index];
-+ }
++ // direct neighbors (6)
++ consumer.accept(front);
++ consumer.accept(back);
++ consumer.accept(right);
++ consumer.accept(left);
++ consumer.accept(below);
++ consumer.accept(above);
+
-+ fillNodeCache(oldCache.length, nodeCache.length);
-+ }
++ // diagonal neighbors (12)
++ consumer.accept(getNeighbor(front, rightward));
++ consumer.accept(getNeighbor(back, leftward));
++ consumer.accept(getNeighbor(front, leftward));
++ consumer.accept(getNeighbor(back, rightward));
++ consumer.accept(getNeighbor(front, downward));
++ consumer.accept(getNeighbor(back, upward));
++ consumer.accept(getNeighbor(front, upward));
++ consumer.accept(getNeighbor(back, downward));
++ consumer.accept(getNeighbor(right, downward));
++ consumer.accept(getNeighbor(left, upward));
++ consumer.accept(getNeighbor(right, upward));
++ consumer.accept(getNeighbor(left, downward));
+
-+ private void fillNodeCache(int start, int end) {
-+ for (int index = start; index < end; index++) {
-+ nodeCache[index] = new Node(level);
-+ }
++ // far neighbors (6)
++ consumer.accept(getNeighbor(front, forward));
++ consumer.accept(getNeighbor(back, backward));
++ consumer.accept(getNeighbor(right, rightward));
++ consumer.accept(getNeighbor(left, leftward));
++ consumer.accept(getNeighbor(below, downward));
++ consumer.accept(getNeighbor(above, upward));
+ }
+
+ /**
@@ -996,7 +1114,7 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ */
+ public void onWireUpdated(BlockPos pos) {
+ invalidateNodes();
-+ findRoots(pos, true);
++ findRoots(pos);
+ tryUpdatePower();
+ }
+
@@ -1014,7 +1132,7 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ wire.added = true;
+
+ invalidateNodes();
-+ findRoots(pos, false);
++ tryAddRoot(wire);
+ tryUpdatePower();
+ }
+
@@ -1036,7 +1154,7 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+
+ // If these fields are set to 'true', the removal of this wire was part of
+ // already ongoing power changes, so we can exit early here.
-+ if (updatingPower && wire.shouldBreak) {
++ if (updating && wire.shouldBreak) {
+ return;
+ }
+
@@ -1053,7 +1171,7 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ * again. This ensures the power calculations of the network are accurate.
+ */
+ private void invalidateNodes() {
-+ if (updatingPower && !nodes.isEmpty()) {
++ if (updating && !nodes.isEmpty()) {
+ Iterator<Entry<Node>> it = Long2ObjectMaps.fastIterator(nodes);
+
+ while (it.hasNext()) {
@@ -1099,7 +1217,7 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ * these optimizations would limit code modifications to the RedStoneWireBlock
+ * and ServerLevel classes while leaving the performance mostly intact.
+ */
-+ private void findRoots(BlockPos pos, boolean checkNeighbors) {
++ private void findRoots(BlockPos pos) {
+ Node node = getOrAddNode(pos);
+
+ if (!node.isWire()) {
@@ -1111,7 +1229,7 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+
+ // If the wire at the given position is not in an invalid state or is not
+ // part of a larger network, we can exit early.
-+ if (!checkNeighbors || !wire.inNetwork || wire.connections.total == 0) {
++ if (!wire.inNetwork || wire.connections.total == 0) {
+ return;
+ }
+
@@ -1130,9 +1248,9 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ }
+
+ /**
-+ * Find signal sources around the given node that can provide direct signals
-+ * to that node, and then search for wires that require power changes around
-+ * those signal sources.
++ * Find signal sources around the given node that can provide direct signals to
++ * that node, and then search for wires that require power changes around those
++ * signal sources.
+ */
+ private void findSignalSourcesAround(Node node, int except) {
+ for (int iDir : Directions.I_EXCEPT[except]) {
@@ -1248,11 +1366,11 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ }
+
+ private int getInitialPower(WireNode wire) {
-+ return (wire.removed || wire.shouldBreak) ? Redstone.SIGNAL_MIN : getExternalPower(wire);
++ return (wire.removed || wire.shouldBreak) ? POWER_MIN : getExternalPower(wire);
+ }
+
+ private int getExternalPower(WireNode wire) {
-+ int power = Redstone.SIGNAL_MIN;
++ int power = POWER_MIN;
+
+ for (int iDir = 0; iDir < Directions.ALL.length; iDir++) {
+ Node neighbor = getNeighbor(wire, iDir);
@@ -1271,8 +1389,8 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ power = Math.max(power, neighbor.state.getSignal(level, neighbor.pos, Directions.ALL[iDir]));
+ }
+
-+ if (power >= Redstone.SIGNAL_MAX) {
-+ return Redstone.SIGNAL_MAX;
++ if (power >= POWER_MAX) {
++ return POWER_MAX;
+ }
+ }
+
@@ -1284,7 +1402,7 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ * through the given conductor node.
+ */
+ private int getDirectSignalTo(WireNode wire, Node node, int except) {
-+ int power = Redstone.SIGNAL_MIN;
++ int power = POWER_MIN;
+
+ for (int iDir : Directions.I_EXCEPT[except]) {
+ Node neighbor = getNeighbor(node, iDir);
@@ -1292,8 +1410,8 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ if (neighbor.isSignalSource()) {
+ power = Math.max(power, neighbor.state.getDirectSignal(level, neighbor.pos, Directions.ALL[iDir]));
+
-+ if (power >= Redstone.SIGNAL_MAX) {
-+ return Redstone.SIGNAL_MAX;
++ if (power >= POWER_MAX) {
++ return POWER_MAX;
+ }
+ }
+ }
@@ -1310,12 +1428,12 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ * the minimum value. This is because it (effectively) no longer exists, so
+ * cannot provide any power to neighboring wires.
+ * <br>
-+ * - Power received from neighboring wires will never exceed {@code maxPower - 1},
-+ * so if the external power is already larger than or equal to that, there is no
-+ * need to check for power from neighboring wires.
++ * - Power received from neighboring wires will never exceed {@code POWER_MAX -
++ * POWER_STEP}, so if the external power is already larger than or equal to
++ * that, there is no need to check for power from neighboring wires.
+ */
+ private void findPower(WireNode wire, boolean ignoreNetwork) {
-+ if (wire.removed || wire.shouldBreak || wire.externalPower >= (Redstone.SIGNAL_MAX - 1)) {
++ if (wire.removed || wire.shouldBreak || wire.externalPower >= (POWER_MAX - POWER_STEP)) {
+ return;
+ }
+
@@ -1339,7 +1457,7 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ WireNode neighbor = connection.wire;
+
+ if (!ignoreNetwork || !neighbor.inNetwork) {
-+ int power = Math.max(Redstone.SIGNAL_MIN, neighbor.virtualPower - 1);
++ int power = Math.max(POWER_MIN, neighbor.virtualPower - POWER_STEP);
+ int iOpp = Directions.iOpposite(connection.iDir);
+
+ wire.offerPower(power, iOpp);
@@ -1355,7 +1473,7 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ if (rootCount > 0) {
+ updatePower();
+ }
-+ if (!updatingPower) {
++ if (!updating) {
+ nodes.clear();
+ nodeCount = 0;
+ }
@@ -1415,7 +1533,7 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ // If anything goes wrong while carrying out power changes, this field must
+ // be reset to 'false', or the wire handler will be locked out of carrying
+ // out power changes until the world is reloaded.
-+ updatingPower = false;
++ updating = false;
+
+ throw t;
+ }
@@ -1480,8 +1598,8 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ WireNode wire = network.get(index);
+ findPower(wire, true);
+
-+ if (index < rootCount || wire.removed || wire.shouldBreak || wire.virtualPower > Redstone.SIGNAL_MIN) {
-+ queuePowerChange(wire);
++ if (index < rootCount || wire.removed || wire.shouldBreak || wire.virtualPower > POWER_MIN) {
++ queueWire(wire);
+ } else {
+ // Wires that do not receive any power do not queue power changes
+ // until they are offered power from a neighboring wire. To ensure
@@ -1494,25 +1612,65 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ }
+
+ /**
-+ * Queue the power change for the given wire. If the wire does not need a power
-+ * change (perhaps because its power has already changed), transmit power to
-+ * neighboring wires.
++ * Carry out power changes, setting the new power of each wire in the world,
++ * notifying neighbors of the power change, then queueing power changes of
++ * connected wires.
+ */
-+ private void queuePowerChange(WireNode wire) {
-+ if (needsPowerChange(wire)) {
-+ powerChanges.offer(wire);
-+ } else {
-+ findPowerFlow(wire);
-+ transmitPower(wire);
++ private void letPowerFlow() {
++ // If an instantaneous update chain causes updates to another network
++ // (or the same network in another place), new power changes will be
++ // integrated into the already ongoing power queue, so we can exit early
++ // here.
++ if (updating) {
++ return;
+ }
++
++ updating = true;
++
++ while (!updates.isEmpty()) {
++ Node node = updates.poll();
++
++ if (node.isWire()) {
++ WireNode wire = node.asWire();
++
++ if (!needsPowerChange(wire)) {
++ continue;
++ }
++
++ findPowerFlow(wire);
++ transmitPower(wire);
++
++ if (wire.setPower()) {
++ queueNeighbors(wire);
++
++ // If the wire was newly placed or removed, shape updates have
++ // already been emitted. However, unlike before 1.19, neighbor
++ // updates are now queued, so to preserve behavior parity with
++ // previous versions, we emit extra shape updates here to
++ // notify neighboring observers.
++ updateNeighborShapes(wire);
++ }
++ } else {
++ WireNode neighborWire = node.neighborWire;
++
++ if (neighborWire != null) {
++ BlockPos neighborPos = neighborWire.pos;
++ Block neighborBlock = neighborWire.state.getBlock();
++
++ updateNeighbor(node, neighborPos, neighborBlock);
++ }
++ }
++ }
++
++ updating = false;
+ }
+
+ /**
+ * Use the information of incoming power flow to determine the direction of
+ * power flow through this wire. If that flow is ambiguous, try to use a flow
+ * direction based on connections to neighboring wires. If that is also
-+ * ambiguous, use the backup value that was set when the wire was first added
-+ * to the network.
++ * ambiguous, use the backup value that was set when the wire was first added to
++ * the network.
+ */
+ private void findPowerFlow(WireNode wire) {
+ int flow = FLOW_IN_TO_FLOW_OUT[wire.flowIn];
@@ -1525,79 +1683,47 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ }
+
+ /**
-+ * Transmit power from the given wire to neighboring wires.
++ * Transmit power from the given wire to neighboring wires and queue updates to
++ * those wires.
+ */
+ private void transmitPower(WireNode wire) {
-+ int nextPower = Math.max(Redstone.SIGNAL_MIN, wire.virtualPower - 1);
-+
+ wire.connections.forEach(connection -> {
+ if (!connection.offer) {
+ return;
+ }
+
+ WireNode neighbor = connection.wire;
++
++ int power = Math.max(POWER_MIN, wire.virtualPower - POWER_STEP);
+ int iDir = connection.iDir;
+
-+ if (neighbor.offerPower(nextPower, iDir)) {
-+ queuePowerChange(neighbor);
++ if (neighbor.offerPower(power, iDir)) {
++ queueWire(neighbor);
+ }
+ }, wire.iFlowDir);
+ }
+
+ /**
-+ * Carry out power changes, setting the new power of each wire in the world,
-+ * notifying neighbors of the power change, then queueing power changes of
-+ * connected wires.
-+ */
-+ private void letPowerFlow() {
-+ // If an instantaneous update chain causes updates to another network
-+ // (or the same network in another place), new power changes will be
-+ // integrated into the already ongoing power queue, so we can exit early
-+ // here.
-+ if (updatingPower) {
-+ return;
-+ }
-+
-+ updatingPower = true;
-+
-+ while (!powerChanges.isEmpty()) {
-+ WireNode wire = powerChanges.poll();
-+
-+ if (!needsPowerChange(wire)) {
-+ continue;
-+ }
-+
-+ findPowerFlow(wire);
-+
-+ if (wire.setPower()) {
-+ // If the wire was newly placed or removed, shape updates have
-+ // already been emitted.
-+ if (!wire.added && !wire.shouldBreak) {
-+ updateNeighborShapes(wire);
-+ }
-+
-+ updateNeighborBlocks(wire);
-+ }
-+
-+ transmitPower(wire);
-+ }
-+
-+ updatingPower = false;
-+ }
-+
-+ /**
+ * Emit shape updates around the given wire.
+ */
+ private void updateNeighborShapes(WireNode wire) {
+ BlockPos wirePos = wire.pos;
+ BlockState wireState = wire.state;
+
-+ for (Direction dir : Block.UPDATE_SHAPE_ORDER) {
-+ updateNeighborShape(wirePos.relative(dir), dir.getOpposite(), wirePos, wireState);
++ for (int iDir : DEFAULT_FULL_UPDATE_ORDER) {
++ Node neighbor = getNeighbor(wire, iDir);
++
++ if (!neighbor.isWire()) {
++ int iOpp = Directions.iOpposite(iDir);
++ Direction opp = Directions.ALL[iOpp];
++
++ updateNeighborShape(neighbor, opp, wirePos, wireState);
++ }
+ }
+ }
+
-+ private void updateNeighborShape(BlockPos pos, Direction fromDir, BlockPos fromPos, BlockState fromState) {
++ private void updateNeighborShape(Node node, Direction fromDir, BlockPos fromPos, BlockState fromState) {
++ BlockPos pos = node.pos;
+ BlockState state = level.getBlockState(pos);
+
+ // Shape updates to redstone wire are very expensive, and should never happen
@@ -1609,81 +1735,44 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ }
+
+ /**
-+ * Emit block updates around the given wire. The order in which neighbors are
-+ * updated is determined as follows:
-+ * <br>
-+ * 1. The direction of power flow through the wire is to be considered 'forward'.
-+ * The order in which neighbors are updated depends on their relative positions
-+ * to the wire.
-+ * <br>
-+ * 2. Each neighbor is identified by the step(s) you must take, starting at the
-+ * wire, to reach it. Each step is 1 block, thus the position of a neighbor is
-+ * encoded by the direction(s) of the step(s), e.g. (right), (down), (up, left),
-+ * etc.
-+ * <br>
-+ * 3. Neighbors are updated in pairs that lie on opposite sides of the wire.
-+ * <br>
-+ * 4. Neighbors are updated in order of their distance from the wire. This means
-+ * they are updated in 3 groups: direct neighbors are updated first, then
-+ * diagonal neighbors, and last are the far neighbors that are 2 blocks directly
-+ * out.
-+ * <br>
-+ * 5. The order within each group is determined using the following basic order:
-+ * { front, back, right, left, down, up }. This order was chosen because it
-+ * converts to the following order of absolute directions when west is said to
-+ * be 'forward': { west, east, north, south, down, up } - this is the order of
-+ * shape updates.
++ * Queue block updates to nodes around the given wire.
+ */
-+ private void updateNeighborBlocks(WireNode wire) {
-+ int iDir = wire.iFlowDir;
-+
-+ Direction forward = Directions.HORIZONTAL[ iDir ];
-+ Direction rightward = Directions.HORIZONTAL[(iDir + 1) & 0b11];
-+ Direction backward = Directions.HORIZONTAL[(iDir + 2) & 0b11];
-+ Direction leftward = Directions.HORIZONTAL[(iDir + 3) & 0b11];
-+ Direction downward = Direction.DOWN;
-+ Direction upward = Direction.UP;
-+
-+ BlockPos self = wire.pos;
-+ BlockPos front = self.relative(forward);
-+ BlockPos right = self.relative(rightward);
-+ BlockPos back = self.relative(backward);
-+ BlockPos left = self.relative(leftward);
-+ BlockPos below = self.relative(downward);
-+ BlockPos above = self.relative(upward);
-+
-+ // direct neighbors (6)
-+ updateNeighbor(front, self);
-+ updateNeighbor(back, self);
-+ updateNeighbor(right, self);
-+ updateNeighbor(left, self);
-+ updateNeighbor(below, self);
-+ updateNeighbor(above, self);
++ private void queueNeighbors(WireNode wire) {
++ forEachNeighbor(wire, neighbor -> {
++ queueNeighbor(neighbor, wire);
++ });
++ }
+
-+ // diagonal neighbors (12)
-+ updateNeighbor(front.relative(rightward), self);
-+ updateNeighbor(back.relative(leftward), self);
-+ updateNeighbor(front.relative(leftward), self);
-+ updateNeighbor(back.relative(rightward), self);
-+ updateNeighbor(front.relative(downward), self);
-+ updateNeighbor(back.relative(upward), self);
-+ updateNeighbor(front.relative(upward), self);
-+ updateNeighbor(back.relative(downward), self);
-+ updateNeighbor(right.relative(downward), self);
-+ updateNeighbor(left.relative(upward), self);
-+ updateNeighbor(right.relative(upward), self);
-+ updateNeighbor(left.relative(downward), self);
++ /**
++ * Queue the given node for an update from the given neighboring wire.
++ */
++ private void queueNeighbor(Node node, WireNode neighborWire) {
++ // Updates to wires are queued when power is transmitted.
++ if (!node.isWire()) {
++ node.neighborWire = neighborWire;
++ updates.offer(node);
++ }
++ }
+
-+ // far neighbors (6)
-+ updateNeighbor(front.relative(forward), self);
-+ updateNeighbor(back.relative(backward), self);
-+ updateNeighbor(right.relative(rightward), self);
-+ updateNeighbor(left.relative(leftward), self);
-+ updateNeighbor(below.relative(downward), self);
-+ updateNeighbor(above.relative(upward), self);
++ /**
++ * Queue the given wire for a power change. If the wire does not need a power
++ * change (perhaps because its power has already changed), transmit power to
++ * neighboring wires.
++ */
++ private void queueWire(WireNode wire) {
++ if (needsPowerChange(wire)) {
++ updates.offer(wire);
++ } else {
++ findPowerFlow(wire);
++ transmitPower(wire);
++ }
+ }
+
-+ private void updateNeighbor(BlockPos pos, BlockPos fromPos) {
++ /**
++ * Emit a block update to the given node.
++ */
++ private void updateNeighbor(Node node, BlockPos fromPos, Block fromBlock) {
++ BlockPos pos = node.pos;
+ BlockState state = level.getBlockState(pos);
+
+ // While this check makes sure wires in the network are not given block
@@ -1696,12 +1785,12 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+ // positions of the network to a set and filter out block updates to wires in
+ // the network that way.
+ if (!state.isAir() && !state.is(Blocks.REDSTONE_WIRE)) {
-+ state.neighborChanged(level, pos, Blocks.REDSTONE_WIRE, fromPos, false);
++ state.neighborChanged(level, pos, fromBlock, fromPos, false);
+ }
+ }
+
+ @FunctionalInterface
-+ public interface NodeProvider {
++ public static interface NodeProvider {
+
+ public Node getNeighbor(Node node, int iDir);
+
@@ -1709,15 +1798,15 @@ index 0000000000000000000000000000000000000000..ef84b462aa4a0cedbf7c3cfb54ba43f3
+}
diff --git a/src/main/java/alternate/current/wire/WireNode.java b/src/main/java/alternate/current/wire/WireNode.java
new file mode 100644
-index 0000000000000000000000000000000000000000..6b5bffd288e2f815d8c3788e73530e69d549e25e
+index 0000000000000000000000000000000000000000..dde98a49b0f4db023386f8e4b98c99340d51c871
--- /dev/null
+++ b/src/main/java/alternate/current/wire/WireNode.java
-@@ -0,0 +1,117 @@
+@@ -0,0 +1,118 @@
+package alternate.current.wire;
+
+import net.minecraft.core.BlockPos;
-+import net.minecraft.util.Mth;
+import net.minecraft.server.level.ServerLevel;
++import net.minecraft.util.Mth;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.RedStoneWireBlock;
@@ -1725,9 +1814,9 @@ index 0000000000000000000000000000000000000000..6b5bffd288e2f815d8c3788e73530e69
+import net.minecraft.world.level.redstone.Redstone;
+
+/**
-+ * A WireNode is a Node that represents a wire in the world. It stores
-+ * all the information about the wire that the WireHandler needs to
-+ * calculate power changes.
++ * A WireNode is a Node that represents a wire in the world. It stores all the
++ * information about the wire that the WireHandler needs to calculate power
++ * changes.
+ *
+ * @author Space Walker
+ */
@@ -1757,13 +1846,6 @@ index 0000000000000000000000000000000000000000..6b5bffd288e2f815d8c3788e73530e69
+ boolean prepared;
+ boolean inNetwork;
+
-+ /** The power for which this wire was queued. */
-+ int power;
-+ /** The previous wire in the power queue. */
-+ WireNode prev;
-+ /** The next wire in the power queue. */
-+ WireNode next;
-+
+ WireNode(ServerLevel level, BlockPos pos, BlockState state) {
+ super(level);
+
@@ -1773,14 +1855,20 @@ index 0000000000000000000000000000000000000000..6b5bffd288e2f815d8c3788e73530e69
+ this.connections = new WireConnectionManager(this);
+
+ this.virtualPower = this.currentPower = this.state.getValue(RedStoneWireBlock.POWER);
++ this.priority = priority();
+ }
+
+ @Override
-+ public Node update(BlockPos pos, BlockState state, boolean clearNeighbors) {
++ Node update(BlockPos pos, BlockState state, boolean clearNeighbors) {
+ throw new UnsupportedOperationException("Cannot update a WireNode!");
+ }
+
+ @Override
++ int priority() {
++ return Mth.clamp(virtualPower, Redstone.SIGNAL_MIN, Redstone.SIGNAL_MAX);
++ }
++
++ @Override
+ public boolean isWire() {
+ return true;
+ }
@@ -1790,10 +1878,6 @@ index 0000000000000000000000000000000000000000..6b5bffd288e2f815d8c3788e73530e69
+ return this;
+ }
+
-+ int nextPower() {
-+ return Mth.clamp(virtualPower, Redstone.SIGNAL_MIN, Redstone.SIGNAL_MAX);
-+ }
-+
+ boolean offerPower(int power, int iDir) {
+ if (removed || shouldBreak) {
+ return false;
@@ -1819,12 +1903,18 @@ index 0000000000000000000000000000000000000000..6b5bffd288e2f815d8c3788e73530e69
+
+ state = level.getBlockState(pos);
+
++ if (!state.is(Blocks.REDSTONE_WIRE)) {
++ return false; // we should never get here
++ }
++
+ if (shouldBreak) {
+ Block.dropResources(state, level, pos);
-+ return level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS);
++ level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS);
++
++ return true;
+ }
+
-+ currentPower = LevelHelper.doRedstoneEvent(level, pos, currentPower, power);
++ currentPower = LevelHelper.doRedstoneEvent(level, pos, currentPower, Mth.clamp(virtualPower, Redstone.SIGNAL_MIN, Redstone.SIGNAL_MAX));
+ state = state.setValue(RedStoneWireBlock.POWER, currentPower);
+
+ return LevelHelper.setWireState(level, pos, state, added);
@@ -2287,18 +2377,9 @@ index a4344bf2267112e3c1e31c07c9f6b8eae9666947..5e973eb53b240615e70b7d46ef4dc17b
public BlockState rotate(BlockState state, Rotation rotation) {
return (BlockState) state.setValue(TripWireHookBlock.FACING, rotation.rotate((Direction) state.getValue(TripWireHookBlock.FACING)));
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-index 61590f2f04a797235299f1bd6b78a08f5bfe4a33..7f83c9390823b42fc30d04e1d3222e2825eaad50 100644
+index 61590f2f04a797235299f1bd6b78a08f5bfe4a33..da7b4783da35bc08f48f2fe31c4d06f9fb5e160f 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-@@ -70,7 +70,7 @@ import net.minecraft.world.phys.shapes.VoxelShape;
-
- public abstract class BlockBehaviour {
-
-- protected static final Direction[] UPDATE_SHAPE_ORDER = new Direction[]{Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH, Direction.DOWN, Direction.UP};
-+ public static final Direction[] UPDATE_SHAPE_ORDER = new Direction[]{Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH, Direction.DOWN, Direction.UP}; // Paper - public
- protected final Material material;
- public final boolean hasCollision;
- protected final float explosionResistance;
@@ -187,6 +187,16 @@ public abstract class BlockBehaviour {
return false;
}