Complete redstone (static subset) specification
Scope
This specification completely describes redstone in minecraft java 1.18.2, with the following blocks:
| Name | ID |
|---|---|
| Air | 0 |
| Solid block | 1 |
| Glass | 2 |
| Slab (top) | 3 |
| Repeater | 4 |
| Comparator | 5 |
| Dust (wire) | 6 |
| Redstone Block | 7 |
| Redstone Torch | 8 |
| Redstone Lamp | 9 |
| Chest (contents static) | 10 |
| Barrel (contents static) | 11 |
Warning
This spec applies exclusively to 1.18.2.
Note
File paths are relative to /src/net/minecraft/ unless stated otherwise.
Types
Scheduled tick
A scheduled tick has five fields: block (the block type it targets), pos (the block position), triggerTick (the game tick it fires on, as a long), priority (a TickPriority value), and subTickOrder (a global sequence number assigned when the tick is scheduled).
Ordering within a chunk's queue (TRIGGER_TICK_COMPARATOR): sort by triggerTick ascending, then priority index ascending (lower index = higher priority), then subTickOrder ascending. This determines the order ticks fire within a chunk.
Ordering across chunks (BASIC_COMPARATOR): BASIC_COMPARATOR compares schedulers using their current head tick, ordering by priority then subTickOrder among already-due ticks.
Deduplication key (identity): only (block, pos) - two ticks for the same block at the same position are considered duplicates regardless of their timing or priority.
Tick priority
| Index | Name |
|---|---|
| −3 | EXTREMELY_HIGH |
| −2 | VERY_HIGH |
| −1 | HIGH |
| 0 | NORMAL |
| 1 | LOW |
| 2 | VERY_LOW |
| 3 | EXTREMELY_LOW |
Lower index = fires first within the same game tick.
Block update flags (1)
| Name | Effect |
|---|---|
| NOTIFY_NEIGHBORS | call Notify neighbors on all 6 adjacent blocks |
| NOTIFY_LISTENERS | send state change to clients |
| FORCE_STATE | skip shape-update propagation (not used by redstone) |
| MOVED | block is moving (pistons; not used here) |
| SKIP_LIGHTING | skip lighting recalculation (not used by redstone) |
- Passed as the
flagsargument to Set block state
Flowchart
%%{init: {"themeVariables": {"fontSize": "80px"}}}%%
flowchart TD
START([Game tick]) --> CLOOP
subgraph COL ["1. Collect chunk schedulers"]
CLOOP{"Next entry in<br/>nextTriggerTickByChunkPos?"}
CLOOP -->|Yes| C1{"storedTriggerTick<br/><= now?"}
C1 -->|No| CLOOP
C1 -->|Yes| C3{Scheduler exists AND has pending tick?}
C3 -->|"No - remove entry"| CLOOP
C3 -->|Yes| C4{"tick.triggerTick<br/><= now?"}
C4 -->|"No - update stored time"| CLOOP
C4 -->|Yes| C5{Chunk loaded?}
C5 -->|No| CLOOP
C5 -->|"Yes - Remove and add to tickableChunkSchedulers"| CLOOP
end
CLOOP -->|"No more"| DLOOP
subgraph DRN ["2. Drain ticks"]
DLOOP{"tickableTicks.size < maxTicks<br/>AND schedulers not empty?"}
DLOOP -->|Yes| D1["Poll top scheduler<br/>Poll its next tick :fontawesome-solid-arrow-right: tickableTicks"]
D1 --> DSLOOP
DSLOOP{"tickableTicks.size < maxTicks AND<br/>candidate tick exists AND<br/>triggerTick <= now AND<br/>sorts before next scheduler's head?"}
DSLOOP -->|Yes| DS1["Poll candidate → tickableTicks"]
DS1 --> DSLOOP
DSLOOP -->|No| D2{Sched has more ticks?}
D2 -->|No| DLOOP
D2 -->|Yes| D3{"Next tick due AND<br/>below maxTicks?"}
D3 -->|"Yes - put back in queue"| DLOOP
D3 -->|"No - record trigger time"| DLOOP
end
DLOOP -->|No| DELAY["Delay remaining:<br/>For each leftover scheduler, record<br/>next triggerTick into nextTriggerTickByChunkPos"]
DELAY --> ELOOP
subgraph EXEC ["3. Execute ticks"]
ELOOP{tickableTicks empty?}
ELOOP -->|No| E1["Poll tick<br/>Remove from copiedList · Add to tickedTicks<br/>Call scheduledTick handler"]
E1 --> ELOOP
end
ELOOP -->|Yes| CLEAR["Clear all queues:<br/>tickableTicks · tickableChunkSchedulers<br/>tickedTicks · copiedList"]
CLEAR --> END([Done])
Procedures
BlockTick - entry point (1)
maxTicks = 65536 (hardcoded at the call site). (2)
- Collect chunk schedulers (3)
- Drain ticks (4)
- Delay remaining ticks (5)
- Loop through
tickableTicksuntil empty: (6) - Poll
tickfrom the front oftickableTicks(7) - Remove
tickfromcopiedTickableTicksListif present (8) - Append
ticktotickedTicks(9) - Tick the block at
tick.pospassing intick.type(10) - Clear
tickableTicks,tickableChunkTickSchedulers,tickedTicks,copiedTickableTicksList(11)
- world/tick/WorldTickScheduler.java:83-93
tick() - server/world/ServerWorld.java:301
- world/tick/WorldTickScheduler.java:96
- world/tick/WorldTickScheduler.java:98
- world/tick/WorldTickScheduler.java:99
- world/tick/WorldTickScheduler.java:181-189
tick(BiConsumer) - world/tick/WorldTickScheduler.java:182
- world/tick/WorldTickScheduler.java:183-185
- world/tick/WorldTickScheduler.java:187
- world/tick/WorldTickScheduler.java:188
- world/tick/WorldTickScheduler.java:193-196
clear()
Collect chunk schedulers (1)
For each entry (chunkKey storedTriggerTick) in nextTriggerTickByChunkPos: (2)
- If
storedTriggerTick> current time: skip this entry (3) - Look up
sched=chunkTickSchedulers[chunkKey]. If not found: remove entry and move on (4) - Peek at the next due tick in
sched. If there is none: remove entry and move on (5) - If that tick's
triggerTick> current time: update the entry's stored time totriggerTickand move on (6) - If the chunk is not loaded (
tickingFutureReadyPredicatereturns false): skip, leaving the entry unchanged (7) - Remove this entry from the map (8)
- Add
schedtotickableChunkTickSchedulers(a priority queue sorted byBASIC_COMPARATORon each scheduler's next-due tick) (9)
- world/tick/WorldTickScheduler.java:102-126
collectTickableChunkTickSchedulers() - world/tick/WorldTickScheduler.java:105
- world/tick/WorldTickScheduler.java:109
- world/tick/WorldTickScheduler.java:110-112
- world/tick/WorldTickScheduler.java:114-116
- world/tick/WorldTickScheduler.java:117-118
- world/tick/WorldTickScheduler.java:119
- world/tick/WorldTickScheduler.java:120
- world/tick/WorldTickScheduler.java:121
Drain ticks (1)
Repeat while tickableTicks has fewer than maxTicks entries and tickableChunkTickSchedulers is not empty: (2)
- Pull the highest-priority scheduler off
tickableChunkTickSchedulers(byBASIC_COMPARATOR). This will be referred to assched(3) - Take the next due tick from
schedand add it totickableTicks(4) - Drain same chunk (
sched) (5) - Peek at the next tick remaining in
sched. If there is none: go to the next iteration (6) - If that tick is due now and
tickableTicksis still belowmaxTicks: putschedback intotickableChunkTickSchedulers(7) - Otherwise: record
(chunkKey(next.pos) :fontawesome-solid-arrow-right: next.triggerTick)innextTriggerTickByChunkPos(8)
- world/tick/WorldTickScheduler.java:128-143
addTickableTicks()(outer) - world/tick/WorldTickScheduler.java:130
- world/tick/WorldTickScheduler.java:130
- world/tick/WorldTickScheduler.java:131-132
- world/tick/WorldTickScheduler.java:133
- world/tick/WorldTickScheduler.java:134-135
- world/tick/WorldTickScheduler.java:136-137
- world/tick/WorldTickScheduler.java:138-140
Drain same chunk (1)
- If
tickableTicksis already atmaxTicks: stop (2) - Peek at the next-due tick of the top scheduler in
tickableChunkTickSchedulers- call itheadTick(null if the queue is empty) (3) - Repeat while
tickableTicksis belowmaxTicks: (4) - Peek at the next tick of
sched- call itcandidate. If none: stop (5) - If
candidate.triggerTick> current time: stop (6) - If
headTickexists andcandidatesorts afterheadTickbyBASIC_COMPARATOR: stop (7) - Take
candidateoffschedand add it totickableTicks(8)
- world/tick/WorldTickScheduler.java:155-170
addTickableTicks()(inner overload) - world/tick/WorldTickScheduler.java:156
- world/tick/WorldTickScheduler.java:157-158
- world/tick/WorldTickScheduler.java:160
- world/tick/WorldTickScheduler.java:161-162
- world/tick/WorldTickScheduler.java:162
- world/tick/WorldTickScheduler.java:162
- world/tick/WorldTickScheduler.java:166-167
Delay remaining ticks (1)
For each scheduler still in tickableChunkTickSchedulers, record (chunkKey(tick.pos) :fontawesome-solid-arrow-right: tick.triggerTick) back into nextTriggerTickByChunkPos. (2)
- world/tick/WorldTickScheduler.java:145-149
delayAllTicks() - world/tick/WorldTickScheduler.java:146-148
Tick the block (1)
- Read the current block state at
pos(2) - If the block type no longer matches the tick's scheduled block type (it was replaced): do nothing and return (3)
- Otherwise run the block's scheduled-tick handler: (4)
- Repeater Repeater scheduled tick
- Comparator Comparator scheduled tick
- Redstone torch (floor) Torch scheduled tick
- Redstone torch (wall) Wall torch scheduled tick
- Redstone lamp Lamp scheduled tick
- Barrel animation update only, no redstone effect
- server/world/ServerWorld.java:654-659
tickBlock() - server/world/ServerWorld.java:655
- server/world/ServerWorld.java:656
- server/world/ServerWorld.java:657
Peek at next tick (1)
Return the tick at the front of the scheduler's tickQueue (lowest by TRIGGER_TICK_COMPARATOR) without removing it. (2) Return null if the queue is empty.
- world/tick/ChunkTickScheduler.java:45-47
peekNextTick() - world/tick/ChunkTickScheduler.java:46
Take next tick (1)
- Remove and return the tick at the front of
tickQueue(lowest byTRIGGER_TICK_COMPARATOR) (2) - If the result is non-null: also remove it from
queuedTicks(3) - Return null if the queue was empty (4)
- world/tick/ChunkTickScheduler.java:50-57
pollNextTick() - world/tick/ChunkTickScheduler.java:51
- world/tick/ChunkTickScheduler.java:52-54
- world/tick/ChunkTickScheduler.java:56
Schedule a tick (1)
ScheduleTick(pos, block, delay, priority = NORMAL)
- Compute
triggerTick= current time +delay(2) - Assign
subTickOrderfrom the world's global monotonic counter (incremented each call) (3) - Find the chunk scheduler for
pos. If the chunk is not loaded: error (4) - If
queuedTicksalready contains a tick for the same(pos, block)pair: return silently (deduplicated) (5) - Add the new tick to
queuedTicksand totickQueue(6) - Notify
WorldTickSchedulerto updatenextTriggerTickByChunkPos[chunkKey(pos)]tomin(stored, triggerTick)(7)
- world/WorldAccess.java:37 / world/tick/ChunkTickScheduler.java:60-71
- world/WorldAccess.java:37
- world/WorldAccess.java:37
- world/tick/WorldTickScheduler.java:73-81
- world/tick/ChunkTickScheduler.java:61
- world/tick/ChunkTickScheduler.java:61-62, 67
- world/tick/ChunkTickScheduler.java:68-70
Set block state (1)
SetBlockState(pos, newState, flags)
- Ask the chunk to store
newStateatpos. If the state did not actually change: return false (2) - As part of that chunk write, the following happen in order:
- The old state's On state replaced handler is called (3)
- The new state's On block added handler is called (4)
- If flag
NOTIFY_LISTENERS(2) is set: send the change to clients (5) - If flag
NOTIFY_NEIGHBORS(1) is set: - Notify neighbors of
pos, citing the old block as the source (6) - If the new state has a comparator output: Notify comparators at
pos(7) - Return true (8)
- world/World.java:189-248
setBlockState() - world/World.java:200-203
- world/chunk/WorldChunk.java:263
- world/chunk/WorldChunk.java:273
- world/World.java:224-228
- world/World.java:230-231
- world/World.java:232-234
- world/World.java:247
Notify neighbors (1)
NotifyNeighbors(pos, sourceBlock)
For each direction in [WEST, EAST, DOWN, UP, NORTH, SOUTH]: notify the block at pos.offset(dir), citing sourceBlock and pos as the origin. (2)
- world/World.java:296-303
updateNeighborsAlways() - world/World.java:297-302
Notify neighbors except one direction (1)
NotifyNeighborsExcept(pos, sourceBlock, exceptDir)
For each direction in [WEST, EAST, DOWN, UP, NORTH, SOUTH] where dir ≠ exceptDir: notify the block at pos.offset(dir). (2)
- world/World.java:305-329
updateNeighborsExcept() - world/World.java:306-328
Notify one neighbor (1)
NotifyNeighbor(pos, sourceBlock, fromPos)
- If running on the client: do nothing (2)
- Read the block state at
pos(3) - Call that block's neighbor-update handler: (4)
- Repeater Repeater neighbor update
- Comparator Comparator neighbor update
- Wire Wire neighbor update
- Redstone torch (floor) Torch neighbor update
- Redstone torch (wall) Wall torch neighbor update
- Redstone lamp Lamp neighbor update
- Any other block: no action within this spec's scope
- world/World.java:331-337
updateNeighbor() - world/World.java:332
- world/World.java:333
- world/World.java:336
Notify comparators (1)
NotifyComparators(pos, sourceBlock)
For each of the 4 horizontal directions [NORTH, EAST, SOUTH, WEST]: (2)
- Check the immediate neighbor in that direction. If it is a comparator: call its neighbor update handler (3)
- Otherwise, if the immediate neighbor is a solid block: check the position one step further in the same direction. If that position is a comparator: call its neighbor update handler (4)
- world/World.java:830-844
updateComparators() - world/World.java:831
- world/World.java:834-836
- world/World.java:837-843
On block added (1)
Called whenever a new block state is written into the chunk. Dispatches on the new block type: (2)
- Repeater Repeater on block added
- Comparator Repeater on block added (same inherited handler from
AbstractRedstoneGateBlock) - Wire Wire on block added
- Redstone torch (floor or wall) Torch on block added
- Any other block: no action within this spec's scope
- world/chunk/WorldChunk.java:272-274
- world/chunk/WorldChunk.java:273
On state replaced (1)
Called whenever a block state is overwritten. Dispatches on the old block type: (2)
- Repeater Repeater on state replaced
- Comparator Repeater on state replaced (same inherited handler)
- Wire Wire on state replaced
- Redstone torch (floor or wall) Torch on state replaced
- Any other block: default behavior (block entity cleanup; no redstone effect)
- world/chunk/WorldChunk.java:262-266
- world/chunk/WorldChunk.java:263
Notify the output side (1)
NotifyOutput(pos, state) - used by repeaters and comparators to push changes to whatever they power.
FACING points toward the input, so FACING.getOpposite() is the output direction.
- Compute
outputDir=state.FACING.getOpposite()andoutputPos=pos.offset(outputDir)(2) - Notify the block at
outputPos, citing this gate as the source (3) - Notify neighbors except one direction at
outputPos- all 5 neighbors of the output block, except the one pointing back toward this gate (directionstate.FACING) (4)
- block/AbstractRedstoneGateBlock.java:169-174
updateTarget() - block/AbstractRedstoneGateBlock.java:170-171
- block/AbstractRedstoneGateBlock.java:172
- block/AbstractRedstoneGateBlock.java:173
Power queries
Emitted power at a position (1)
EmittedPower(pos, dir) dir is the direction from the querying block toward pos.
- Read the block state at
pos(2) - Get the block's weak power output in direction
dir(3) - If the block at
posis a solid opaque block: return the maximum of that weak power and the strong power received atpos(4) - Otherwise: return the weak power directly (5)
A solid opaque block re-emits as weak power any strong power arriving from any face.
- world/World.java:680-684
getEmittedRedstonePower() - world/World.java:681
- world/World.java:682
- world/World.java:683
- world/World.java:683
Strong power received at a position (1)
StrongPowerReceived(pos)
For each direction in [DOWN, UP, NORTH, SOUTH, WEST, EAST]: get the strong power output of the neighbor in that direction, track the running maximum, and return 15 immediately if it is reached. (2) Return the maximum found.
- world/World.java:645-674
getReceivedStrongRedstonePower() - world/World.java:647-673
Weak power received at a position (1)
WeakPowerReceived(pos)
For each direction in [DOWN, UP, NORTH, SOUTH, WEST, EAST]: get the emitted power from the neighbor in that direction, track the running maximum, and return 15 immediately if it is reached. (2) Return the maximum found.
- world/World.java:700-715
getReceivedRedstonePower() - world/World.java:703-712
Is the position receiving any power? (1)
For each direction in [DOWN, UP, NORTH, SOUTH, WEST, EAST]: if the emitted power from the neighbor in that direction is > 0, return true. (2) Otherwise return false.
- world/World.java:686-698
isReceivingRedstonePower() - world/World.java:687-696
Is the position emitting power in a direction? (1)
Return true if EmittedPower(pos, dir) > 0. (2)
- world/World.java:676-678
isEmittingRedstonePower() - world/World.java:677
Weak power output (1)
WeakPower(state, pos, dir) - how much weak power this block emits in direction dir.
- Air (0), Solid block (1), Glass (2), Slab (3): 0
- Repeater (4): Repeater weak power
- Comparator (5): Comparator weak power
- Wire (6): Wire weak power
- Redstone block (7): 15 in all directions
- Redstone torch floor (8): Torch weak power
- Redstone torch wall (8w): Wall torch weak power
- Redstone lamp (9), Chest (10), Barrel (11): 0
BlockState.getWeakRedstonePower- dispatched by block type; see individual block sections
Strong power output (1)
StrongPower(state, pos, dir) - how much strong power this block emits in direction dir.
- Air (0), Solid block (1), Glass (2), Slab (3): 0
- Repeater (4): same as Repeater weak power
- Comparator (5): same as Comparator weak power
- Wire (6): Wire strong power
- Redstone block (7): 0 (does not override strong power)
- Redstone torch floor (8): Torch weak power if
dir== DOWN, else 0 (2) - Redstone torch wall (8w): Wall torch weak power if
dir== DOWN, else 0 (3) - Redstone lamp (9), Chest (10), Barrel (11): 0
BlockState.getStrongRedstonePower- dispatched by block type; see individual block sections- block/RedstoneTorchBlock.java:86-88
- block/RedstoneTorchBlock.java:86-88 (inherited by WallRedstoneTorchBlock)
Block 0 - Air
No behavior. Emits no power.
Block 1 - Solid block (opaque)
No redstone behavior of its own. Emits no weak or strong power. isSolidBlock = true, so it conducts strong power from neighboring emitters - handled automatically by Emitted power at a position.
Block 2 - Glass
No redstone behavior. Emits no power. isSolidBlock = false, so it does not conduct strong power.
Block 3 - Slab (top)
No redstone behavior. Emits no power. isSolidBlock = false. Wire can be placed on top of it.
Block 4 - Repeater
State: FACING (NORTH/EAST/SOUTH/WEST), DELAY (1-4 game ticks), LOCKED (bool), POWERED (bool)
graph LR
A[Repeater] -->|`FACING`| B[Input]
C[Output] --> A
FACING points toward the input block. The output block sits at pos.offset(FACING.getOpposite()). When placed, FACING = playerFacing.getOpposite().
Repeater weak power (1)
- If the repeater is not powered: return 0 (2)
- If
dir==FACING(the querying block is on the output side): return 15 (3) - Otherwise: return 0 (4)
- block/AbstractRedstoneGateBlock.java:59-65
getWeakRedstonePower() - block/AbstractRedstoneGateBlock.java:60-61
- block/AbstractRedstoneGateBlock.java:63
- block/AbstractRedstoneGateBlock.java:63
Repeater front input power (1)
- Get the emitted power of the block directly in front (
pos.offset(FACING)), measured in theFACINGdirection (2) - If that power is less than 15 and the block in front is wire: take the maximum of the emitted power and the wire's
POWERstate value (3) - Return the result (4)
- block/AbstractRedstoneGateBlock.java:107-117
getPower() - block/AbstractRedstoneGateBlock.java:108-110
- block/AbstractRedstoneGateBlock.java:111-116
- block/AbstractRedstoneGateBlock.java:116
Repeater side input power (1)
Check the two blocks perpendicular to FACING: left = FACING.rotateYClockwise(), right = FACING.rotateYCounterclockwise(). (2)
Return the maximum of repeater side input level for each side. (3)
- block/AbstractRedstoneGateBlock.java:119-124
getMaxInputLevelSides() - block/AbstractRedstoneGateBlock.java:120-122
- block/AbstractRedstoneGateBlock.java:123
Repeater side input level (1)
For repeaters, only another repeater or comparator on the side can contribute a locking signal.
- Read the block state at
sidePos(2) - If that block is not a repeater or comparator: return 0 (3)
- Return the strong power output of that block in
sideDir(4)
- block/AbstractRedstoneGateBlock.java:126-137
getInputLevel()(repeater variant,isValidInput=isRedstoneGate) - block/AbstractRedstoneGateBlock.java:127
- block/AbstractRedstoneGateBlock.java:128, 134-136
- block/AbstractRedstoneGateBlock.java:132
Repeater locked check (1)
Return true if repeater side input power > 0. (2)
- block/RepeaterBlock.java:60-62
isLocked() - block/RepeaterBlock.java:61
Does the repeater face a misaligned gate? (1)
- The output block sits at
pos.offset(FACING.getOpposite())(2) - Return true if that block is a repeater or comparator and its
FACINGis not pointing back toward this repeater (3)
- block/AbstractRedstoneGateBlock.java:188-192
isTargetNotAligned() - block/AbstractRedstoneGateBlock.java:189-190
- block/AbstractRedstoneGateBlock.java:191
Repeater neighbor update (1)
- If the repeater has no floor to stand on: drop as an item, remove the block, notify all 6 neighbors, and stop (2)
- Otherwise: check and schedule a state change (3)
- block/AbstractRedstoneGateBlock.java:68-80
neighborUpdate() - block/AbstractRedstoneGateBlock.java:71-79
- block/AbstractRedstoneGateBlock.java:70
Repeater check and schedule (1)
- If the repeater is locked: do nothing (2)
- If the current
POWEREDstate already matches whether front input power > 0, or a tick for this repeater is already being processed this game tick: do nothing (3) - Determine scheduling priority: (4)
- If the output target is misaligned: EXTREMELY_HIGH (5)
- Else if currently powered: VERY_HIGH (6)
- Otherwise: HIGH (7)
- Schedule a tick for this repeater at
DELAY × 2game ticks from now with that priority (8)
- block/AbstractRedstoneGateBlock.java:82-97
updatePowered() - block/AbstractRedstoneGateBlock.java:83
- block/AbstractRedstoneGateBlock.java:84-86
- block/AbstractRedstoneGateBlock.java:87-93
- block/AbstractRedstoneGateBlock.java:88-89
- block/AbstractRedstoneGateBlock.java:90-91
- block/AbstractRedstoneGateBlock.java:87
- block/AbstractRedstoneGateBlock.java:94
Repeater scheduled tick (1)
- If the repeater is locked: do nothing (2)
- Read current
POWEREDstate and whether front input power > 0 (call ithasPower) (3) - If powered and
hasPoweris false: set block state toPOWERED=false, flags 2 (4) - If not powered: (5)
- Set block state to
POWERED=true, flags 2 (6) - If
hasPoweris also false (input disappeared before this tick fired): schedule a tick atDELAY × 2ticks with priority VERY_HIGH to turn off again (7)
- block/AbstractRedstoneGateBlock.java:38-51
scheduledTick() - block/AbstractRedstoneGateBlock.java:39
- block/AbstractRedstoneGateBlock.java:40-41
- block/AbstractRedstoneGateBlock.java:42-43
- block/AbstractRedstoneGateBlock.java:44
- block/AbstractRedstoneGateBlock.java:45
- block/AbstractRedstoneGateBlock.java:46-48
Repeater on block added (1)
- block/AbstractRedstoneGateBlock.java:157-159
onBlockAdded() - block/AbstractRedstoneGateBlock.java:158
Repeater on state replaced (1)
- If the new block is the same type (e.g., a
POWEREDorDELAYchange): do nothing - the output was already notified via on block added (2) - Otherwise: notify the output side using the old state (3)
- block/AbstractRedstoneGateBlock.java:162-167
onStateReplaced() - block/AbstractRedstoneGateBlock.java:163
- block/AbstractRedstoneGateBlock.java:165
Block 5 - Comparator
State: FACING (NORTH/EAST/SOUTH/WEST), MODE (COMPARE/SUBTRACT), POWERED (bool)
FACING follows the same convention as the repeater - points toward the input, output at pos.offset(FACING.getOpposite()).
Block entity: ComparatorBlockEntity stores outputSignal (int 0-15) - the comparator's last computed output value.
Comparator weak power (1)
- If the comparator is not powered: return 0 (2)
- If
dir==FACING(the querying block is on the output side): returnComparatorBlockEntity(pos).outputSignal(3) - Otherwise: return 0 (4)
- block/AbstractRedstoneGateBlock.java:59-65 / block/ComparatorBlock.java:41-44
getWeakRedstonePower()/getOutputLevel() - block/AbstractRedstoneGateBlock.java:60-61
- block/AbstractRedstoneGateBlock.java:63 / block/ComparatorBlock.java:43
- block/AbstractRedstoneGateBlock.java:63
Comparator front input power (1)
- Start with the emitted power of the block directly in front (
pos.offset(FACING)), measured in theFACINGdirection (2) - Check the block directly in front: (3)
- If it outputs a comparator signal (e.g., a container): use that signal as the power value (4)
- Otherwise, if the current power is less than 15 and the front block is solid: look one step further behind it (
behindPos = frontPos.offset(FACING)): (5)- Check for an item frame at
behindPosfacingFACING(only counts if exactly one frame is present) (6) - Check if the block at
behindPosoutputs a comparator signal (7) - Use the larger of the two (if either exists) (8)
- Check for an item frame at
- Return the resulting power value (9)
- block/ComparatorBlock.java:72-92
getPower() - block/ComparatorBlock.java:73
- block/ComparatorBlock.java:74-76
- block/ComparatorBlock.java:77-78
- block/ComparatorBlock.java:79-88
- block/ComparatorBlock.java:82
- block/ComparatorBlock.java:84
- block/ComparatorBlock.java:83-87
- block/ComparatorBlock.java:91
Comparator side input power (1)
Check the two perpendicular neighbors (left and right of FACING). Comparators accept side input from any neighboring block capable of emitting redstone power, using special handling for redstone blocks and wire. (2)
Return the maximum of comparator side input level for each side. (3)
- block/AbstractRedstoneGateBlock.java:119-124
getMaxInputLevelSides()(comparator variant) - block/AbstractRedstoneGateBlock.java:120-122
- block/AbstractRedstoneGateBlock.java:123
Comparator side input level (1)
- If the side block does not emit redstone power: return 0 (2)
- If it is a redstone block: return 15 (3)
- If it is wire: return the wire's
POWERstate value (4) - Otherwise: return its strong power output in
sideDir(5)
- block/AbstractRedstoneGateBlock.java:126-137
getInputLevel()(comparator variant,isValidInput=emitsRedstonePower) - block/AbstractRedstoneGateBlock.java:128, 134-136
- block/AbstractRedstoneGateBlock.java:129-130
- block/AbstractRedstoneGateBlock.java:132
- block/AbstractRedstoneGateBlock.java:132
Comparator output signal (1)
- Get front input power. If it is 0: return 0 (2)
- Get side input power (3)
- If side > front: return 0 (4)
- If mode is SUBTRACT: return
front − side. If mode is COMPARE: returnfront(5)
- block/ComparatorBlock.java:46-58
calculateOutputSignal() - block/ComparatorBlock.java:47-49
- block/ComparatorBlock.java:51
- block/ComparatorBlock.java:52-53
- block/ComparatorBlock.java:55
Does the comparator have power? (1)
- Get front input power. If 0: return false (2)
- Get side input power (3)
- Return true if
front > side, or iffront == sideand mode is COMPARE (4)
- block/ComparatorBlock.java:61-69
hasPower() - block/ComparatorBlock.java:62-65
- block/ComparatorBlock.java:67
- block/ComparatorBlock.java:68
Comparator neighbor update (1)
- If a tick for this comparator is already being processed this game tick: do nothing (2)
- Compute the new output signal and read the old one from the block entity (0 if absent) (3)
- If the output signal hasn't changed and the
POWEREDstate already matches whether it has power: do nothing (4) - Otherwise schedule a tick at 2 game ticks from now: (5)
- Priority HIGH if the output target is misaligned (6)
- Priority NORMAL otherwise (7)
- block/ComparatorBlock.java:119-129
updatePowered() - block/ComparatorBlock.java:120
- block/ComparatorBlock.java:121-123
- block/ComparatorBlock.java:124
- block/ComparatorBlock.java:126
- block/ComparatorBlock.java:125
- block/ComparatorBlock.java:125
Comparator scheduled tick (1)
- Compute the new output signal (2)
- Read the old
outputSignalfrom the block entity (0 if absent) (3) - Write the new value to the block entity (4)
- If the output signal didn't change and mode is not COMPARE: stop (no state update needed) (5)
- Check whether the comparator has power (6)
- If currently
POWEREDbut no longer has power: set block state toPOWERED=false, flags 2 (7) - If currently not
POWEREDbut now has power: set block state toPOWERED=true, flags 2 (8) - Notify the output side (9)
- block/ComparatorBlock.java:154-156
update()at 131-151 - block/ComparatorBlock.java:132
- block/ComparatorBlock.java:133-136
- block/ComparatorBlock.java:137
- block/ComparatorBlock.java:140
- block/ComparatorBlock.java:141
- block/ComparatorBlock.java:143-144
- block/ComparatorBlock.java:145-146
- block/ComparatorBlock.java:149
Block 6 - Wire (Redstone Dust)
State: WIRE_CONNECTION_{NORTH,EAST,SOUTH,WEST} (NONE/SIDE/UP), POWER (0-15)
wiresGivePower flag: a mutable boolean on the wire block singleton. Normally true; temporarily set to false during Wire received power to prevent circular power queries.
Wire weak power (1)
- If
wiresGivePoweris false, ordir== DOWN: return 0 (2) - If
POWER== 0: return 0 (3) - If
dir== UP, or the wire is connected toward the querying block (connection in directiondir.getOpposite()is SIDE or UP): returnPOWER(4) - Otherwise: return 0 (5)
- block/RedstoneWireBlock.java:404-418
getWeakRedstonePower() - block/RedstoneWireBlock.java:405
- block/RedstoneWireBlock.java:407-408
- block/RedstoneWireBlock.java:410-413
- block/RedstoneWireBlock.java:410-413
Wire strong power (1)
- If
wiresGivePoweris false: return 0 (2) - Otherwise: return wire weak power (3)
- block/RedstoneWireBlock.java:399-401
getStrongRedstonePower() - block/RedstoneWireBlock.java:400
- block/RedstoneWireBlock.java:400
Wire received power (1)
- Temporarily set
wiresGivePower= false (2) - Get weak power received at
pos- finds power from all non-wire sources on all 6 faces. Call thisdirect(3) - Restore
wiresGivePower= true (4) - If
direct< 15, also check adjacent wires: (5) - For each of [NORTH, EAST, SOUTH, WEST]: (6)
- Check the horizontal neighbor: if it is wire, note its
POWER(7) - If the neighbor is a solid block and the block directly above
posis not solid: also check the wire one step up from that neighbor (8) - If the neighbor is not solid: also check the wire one step down from that neighbor (9)
- Check the horizontal neighbor: if it is wire, note its
- Track the maximum wire
POWERseen; call thisadjacent(10) - Return
max(direct, adjacent − 1)(11)
- block/RedstoneWireBlock.java:307-327
getReceivedRedstonePower() - block/RedstoneWireBlock.java:308
- block/RedstoneWireBlock.java:309
- block/RedstoneWireBlock.java:310
- block/RedstoneWireBlock.java:312
- block/RedstoneWireBlock.java:313
- block/RedstoneWireBlock.java:316
- block/RedstoneWireBlock.java:318-319
- block/RedstoneWireBlock.java:320-321
- block/RedstoneWireBlock.java:316-321
- block/RedstoneWireBlock.java:326
Wire update (1)
- Compute wire received power (2)
- If
POWERalready equals the new value: do nothing (3) - If the block state at
posis still the same: set block state with the newPOWERvalue, flags 2 (4) - Build the set of positions to notify:
positself plus all 6 adjacent positions (5) - For each of those 7 positions: notify neighbors citing Wire as the source (6)
- block/RedstoneWireBlock.java:287-305
update() - block/RedstoneWireBlock.java:288
- block/RedstoneWireBlock.java:289
- block/RedstoneWireBlock.java:290-292
- block/RedstoneWireBlock.java:294-299
- block/RedstoneWireBlock.java:301-303
Wire neighbor update (1)
- If running on the client: do nothing (2)
- If the wire has no surface to sit on: drop as an item, remove the block, and stop (3)
- Otherwise: wire update (4)
- block/RedstoneWireBlock.java:387-396
neighborUpdate() - block/RedstoneWireBlock.java:388
- block/RedstoneWireBlock.java:392-394
- block/RedstoneWireBlock.java:390
Wire on block added (1)
- If running on the client: do nothing (2)
- Wire update (3)
- Notify neighbors of
pos.up()andpos.down(), citing Wire (4) - Notify wire offset neighbors at
pos(5)
- block/RedstoneWireBlock.java:344-354
onBlockAdded() - block/RedstoneWireBlock.java:345
- block/RedstoneWireBlock.java:346
- block/RedstoneWireBlock.java:348-350
- block/RedstoneWireBlock.java:352
Wire on state replaced (1)
- If the block is moving, or the new block is also wire: do nothing (2)
- If running on the client: do nothing (3)
- For each of the 6 directions: notify neighbors of
pos.offset(dir), citing Wire (4) - Wire update using the old state (5)
- Notify wire offset neighbors at
pos(6)
- block/RedstoneWireBlock.java:357-369
onStateReplaced() - block/RedstoneWireBlock.java:358
- block/RedstoneWireBlock.java:360
- block/RedstoneWireBlock.java:361-363
- block/RedstoneWireBlock.java:365
- block/RedstoneWireBlock.java:366
Notify wire offset neighbors (1)
For each horizontal direction [NORTH, EAST, SOUTH, WEST]: (2)
- If the immediate neighbor is wire: notify neighbors of that neighbor and all 6 of its own neighbors, citing Wire (3)
Then for each horizontal direction [NORTH, EAST, SOUTH, WEST]: (4)
- If the neighbor is a solid block, and the block one step above that neighbor is wire: notify neighbors of that wire and all 6 of its neighbors, citing Wire (5)
- If the neighbor is not a solid block, and the block one step below that neighbor is wire: notify neighbors of that wire and all 6 of its neighbors, citing Wire (6)
- block/RedstoneWireBlock.java:371-384
updateOffsetNeighbors() - block/RedstoneWireBlock.java:372
- block/RedstoneWireBlock.java:373-374
- block/RedstoneWireBlock.java:376
- block/RedstoneWireBlock.java:378-379
- block/RedstoneWireBlock.java:380-382
Block 7 - Redstone Block
No scheduled tick. No neighbor-update handler. Emits weak power 15 in all directions. Does not emit strong power (isSolidBlock = true but strong power is not overridden). emitsRedstonePower = true.
Block 8 - Redstone Torch (floor)
State: LIT (bool), default true
Torch weak power (1)
Return 15 if lit and dir ≠ UP, otherwise return 0. (2)
- block/RedstoneTorchBlock.java:48-50
getWeakRedstonePower() - block/RedstoneTorchBlock.java:49
Should the torch turn off? (1)
Return true if the block the torch is mounted on is emitting power downward - i.e., the mounting block is powered. (2)
- block/RedstoneTorchBlock.java:52-54
shouldUnpower() - block/RedstoneTorchBlock.java:53
Torch neighbor update (1)
If LIT == shouldUnpower (meaning a state change is needed: either lit and should turn off, or unlit and should turn on), and no tick is already scheduled for this torch: schedule a tick at 2 game ticks, priority NORMAL. (2)
- block/RedstoneTorchBlock.java:79-83
neighborUpdate() - block/RedstoneTorchBlock.java:80-82
Torch scheduled tick (1)
- Check whether the torch should turn off (2)
- Remove any burnout entries older than 60 game ticks from the burnout table (3)
- If lit and should turn off: (4)
- Set block state to
LIT=false, flags 3 (5) - If the torch is now burned out (recording this event): schedule a tick at 160 game ticks, NORMAL - prevents rapid relighting (6)
- If unlit and should turn on, and the torch is not burned out (not recording): set block state to
LIT=true, flags 3 (7)
- block/RedstoneTorchBlock.java:57-76
scheduledTick() - block/RedstoneTorchBlock.java:58
- block/RedstoneTorchBlock.java:59-63
- block/RedstoneTorchBlock.java:65-66
- block/RedstoneTorchBlock.java:67
- block/RedstoneTorchBlock.java:68-71
- block/RedstoneTorchBlock.java:73-75
Is the torch burned out? (1)
IsBurnedOut(pos, addNew) - tracks how many times this torch has turned off in the last 60 game ticks.
- Look up this position's entry list in the per-world burnout table (create it if absent) (2)
- If
addNewis true: append an entry with the current game tick (3) - Count entries for this position. If 8 or more: return true (burned out); otherwise false (4)
- block/RedstoneTorchBlock.java:110-128
isBurnedOut() - block/RedstoneTorchBlock.java:111
- block/RedstoneTorchBlock.java:112-114
- block/RedstoneTorchBlock.java:116-127
Torch on block added (1)
For each of the 6 directions: notify neighbors at pos.offset(dir), citing RedstoneTorch. (2)
- block/RedstoneTorchBlock.java:32-36
onBlockAdded() - block/RedstoneTorchBlock.java:33-35
Torch on state replaced (1)
If the block is not moving: for each of the 6 directions, notify neighbors at pos.offset(dir), citing RedstoneTorch. (2) (Fires for LIT changes as well as full block removal.)
- block/RedstoneTorchBlock.java:39-45
onStateReplaced() - block/RedstoneTorchBlock.java:40-44
Block 8w - Wall Redstone Torch
State: FACING (NORTH/EAST/SOUTH/WEST), LIT (bool), default true
FACING is the direction the torch points - away from the wall. The mounting block is at pos.offset(FACING.getOpposite()).
Wall torch weak power (1)
Return 15 if lit and dir ≠ FACING, otherwise return 0. (2)
- block/WallRedstoneTorchBlock.java:76-78
getWeakRedstonePower() - block/WallRedstoneTorchBlock.java:77
Should the wall torch turn off? (1)
mountDir=FACING.getOpposite()(2)- Return true if the mounting block at
pos.offset(mountDir)is emitting power in directionmountDir(3)
- block/WallRedstoneTorchBlock.java:70-73
shouldUnpower() - block/WallRedstoneTorchBlock.java:71
- block/WallRedstoneTorchBlock.java:72
Wall torch neighbor update
Same logic as Torch neighbor update but uses wall torch's should-turn-off check and schedules for WallRedstoneTorch. (Inherited from RedstoneTorchBlock.neighborUpdate - block/RedstoneTorchBlock.java:79-83.)
Wall torch scheduled tick
Same logic as Torch scheduled tick but uses wall torch's should-turn-off check and writes WallRedstoneTorch states. (Inherited from RedstoneTorchBlock.scheduledTick - block/RedstoneTorchBlock.java:57-76.)
Block 9 - Redstone Lamp
State: LIT (bool), default false. Never emits power.
Lamp neighbor update (1)
- If running on the client: do nothing (2)
- Read
lit=state.LITandreceiving= is the position receiving any power? (3) - If
lit==receiving: do nothing (state is already correct) (4) - If the lamp is lit and signal was lost: schedule a tick at 4 game ticks, priority NORMAL (5)
- If the lamp is unlit and signal arrived: set block state to
LIT=true, flags 2 (immediate, no delay) (6)
- block/RedstoneLampBlock.java:27-38
neighborUpdate() - block/RedstoneLampBlock.java:28
- block/RedstoneLampBlock.java:29-30
- block/RedstoneLampBlock.java:30
- block/RedstoneLampBlock.java:31-32
- block/RedstoneLampBlock.java:33-34
Lamp scheduled tick (1)
If lit and no longer receiving power: set block state to LIT=false, flags 2. (2)
- block/RedstoneLampBlock.java:41-45
scheduledTick() - block/RedstoneLampBlock.java:42-44
Block 10 - Chest
hasComparatorOutput = true. Otherwise passive (contents are static in this spec).
Chest comparator output (1)
Get the chest's inventory - for a double chest, this is the combined 54-slot inventory. Return the inventory fill signal. (2)
- block/ChestBlock.java:351-352
getComparatorOutput() - block/ChestBlock.java:352
Block 11 - Barrel
hasComparatorOutput = true. Otherwise passive.
Barrel comparator output (1)
Get the barrel's block entity and return the inventory fill signal. (2)
- block/BarrelBlock.java:103-104
getComparatorOutput() - block/BarrelBlock.java:104
Inventory fill signal (1)
- If the inventory is null: return 0 (2)
- For each slot: if not empty, add
(item count) / min(inventory max stack size, item's own max stack size)to a running total, and count one non-empty slot (3) - Divide the total by the number of slots to get a fill ratio (4)
- Return
floor(fillRatio × 14) + (1 if any slot was non-empty, else 0)(5)
Range: 0 (completely empty) to 15 (completely full). Single chest: 27 slots. Double chest: 54 slots. Barrel: 27 slots.
- screen/ScreenHandler.java:707-725
calculateComparatorOutput() - screen/ScreenHandler.java:708-710
- screen/ScreenHandler.java:715-721
- screen/ScreenHandler.java:723
- screen/ScreenHandler.java:724