Skip to content

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): sort by priority index ascending, then subTickOrder ascending. Used to fairly interleave ticks from different chunks.

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)
  1. Passed as the flags argument 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/>&lt;= now?"}
        C1 -->|No| CLOOP
        C1 -->|Yes| C3{Scheduler exists AND has pending tick?}
        C3 -->|"No - remove entry"| CLOOP
        C3 -->|Yes| C4{"tick.triggerTick<br/>&lt;= 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 &lt; 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 &lt; maxTicks AND<br/>candidate tick exists AND<br/>triggerTick &lt;= 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)

  1. Collect chunk schedulers (3)
  2. Drain ticks (4)
  3. Delay remaining ticks (5)
  4. Loop through tickableTicks until empty: (6)
  5. Poll tick from the front of tickableTicks (7)
  6. Remove tick from copiedTickableTicksList if present (8)
  7. Append tick to tickedTicks (9)
  8. Tick the block at tick.pos passing in tick.type (10)
  9. Clear tickableTicks, tickableChunkTickSchedulers, tickedTicks, copiedTickableTicksList (11)
  1. world/tick/WorldTickScheduler.java:83-93 tick()
  2. server/world/ServerWorld.java:301
  3. world/tick/WorldTickScheduler.java:96
  4. world/tick/WorldTickScheduler.java:98
  5. world/tick/WorldTickScheduler.java:99
  6. world/tick/WorldTickScheduler.java:181-189 tick(BiConsumer)
  7. world/tick/WorldTickScheduler.java:182
  8. world/tick/WorldTickScheduler.java:183-185
  9. world/tick/WorldTickScheduler.java:187
  10. world/tick/WorldTickScheduler.java:188
  11. world/tick/WorldTickScheduler.java:193-196 clear()

Collect chunk schedulers (1)

For each entry (chunkKey storedTriggerTick) in nextTriggerTickByChunkPos: (2)

  1. If storedTriggerTick > current time: skip this entry (3)
  2. Look up sched = chunkTickSchedulers[chunkKey]. If not found: remove entry and move on (4)
  3. Peek at the next due tick in sched. If there is none: remove entry and move on (5)
  4. If that tick's triggerTick > current time: update the entry's stored time to triggerTick and move on (6)
  5. If the chunk is not loaded (tickingFutureReadyPredicate returns false): skip, leaving the entry unchanged (7)
  6. Remove this entry from the map (8)
  7. Add sched to tickableChunkTickSchedulers (a priority queue sorted by BASIC_COMPARATOR on each scheduler's next-due tick) (9)
  1. world/tick/WorldTickScheduler.java:102-126 collectTickableChunkTickSchedulers()
  2. world/tick/WorldTickScheduler.java:105
  3. world/tick/WorldTickScheduler.java:109
  4. world/tick/WorldTickScheduler.java:110-112
  5. world/tick/WorldTickScheduler.java:114-116
  6. world/tick/WorldTickScheduler.java:117-118
  7. world/tick/WorldTickScheduler.java:119
  8. world/tick/WorldTickScheduler.java:120
  9. world/tick/WorldTickScheduler.java:121

Drain ticks (1)

Repeat while tickableTicks has fewer than maxTicks entries and tickableChunkTickSchedulers is not empty: (2)

  1. Pull the highest-priority scheduler off tickableChunkTickSchedulers (by BASIC_COMPARATOR). This will be referred to as sched (3)
  2. Take the next due tick from sched and add it to tickableTicks (4)
  3. Drain same chunk (sched) (5)
  4. Peek at the next tick remaining in sched. If there is none: go to the next iteration (6)
  5. If that tick is due now and tickableTicks is still below maxTicks: put sched back into tickableChunkTickSchedulers (7)
  6. Otherwise: record (chunkKey(next.pos) :fontawesome-solid-arrow-right: next.triggerTick) in nextTriggerTickByChunkPos (8)
  1. world/tick/WorldTickScheduler.java:128-143 addTickableTicks() (outer)
  2. world/tick/WorldTickScheduler.java:130
  3. world/tick/WorldTickScheduler.java:130
  4. world/tick/WorldTickScheduler.java:131-132
  5. world/tick/WorldTickScheduler.java:133
  6. world/tick/WorldTickScheduler.java:134-135
  7. world/tick/WorldTickScheduler.java:136-137
  8. world/tick/WorldTickScheduler.java:138-140

Drain same chunk (1)

  1. If tickableTicks is already at maxTicks: stop (2)
  2. Peek at the next-due tick of the top scheduler in tickableChunkTickSchedulers - call it headTick (null if the queue is empty) (3)
  3. Repeat while tickableTicks is below maxTicks: (4)
  4. Peek at the next tick of sched - call it candidate. If none: stop (5)
  5. If candidate.triggerTick > current time: stop (6)
  6. If headTick exists and candidate sorts after headTick by BASIC_COMPARATOR: stop (7)
  7. Take candidate off sched and add it to tickableTicks (8)
  1. world/tick/WorldTickScheduler.java:155-170 addTickableTicks() (inner overload)
  2. world/tick/WorldTickScheduler.java:156
  3. world/tick/WorldTickScheduler.java:157-158
  4. world/tick/WorldTickScheduler.java:160
  5. world/tick/WorldTickScheduler.java:161-162
  6. world/tick/WorldTickScheduler.java:162
  7. world/tick/WorldTickScheduler.java:162
  8. 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)

  1. world/tick/WorldTickScheduler.java:145-149 delayAllTicks()
  2. world/tick/WorldTickScheduler.java:146-148

Tick the block (1)

  1. Read the current block state at pos (2)
  2. If the block type no longer matches the tick's scheduled block type (it was replaced): do nothing and return (3)
  3. Otherwise run the block's scheduled-tick handler: (4)
  4. Repeater Repeater scheduled tick
  5. Comparator Comparator scheduled tick
  6. Redstone torch (floor) Torch scheduled tick
  7. Redstone torch (wall) Wall torch scheduled tick
  8. Redstone lamp Lamp scheduled tick
  9. Barrel animation update only, no redstone effect
  1. server/world/ServerWorld.java:654-659 tickBlock()
  2. server/world/ServerWorld.java:655
  3. server/world/ServerWorld.java:656
  4. 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.

  1. world/tick/ChunkTickScheduler.java:45-47 peekNextTick()
  2. world/tick/ChunkTickScheduler.java:46

Take next tick (1)

  1. Remove and return the tick at the front of tickQueue (lowest by TRIGGER_TICK_COMPARATOR) (2)
  2. If the result is non-null: also remove it from queuedTicks (3)
  3. Return null if the queue was empty (4)
  1. world/tick/ChunkTickScheduler.java:50-57 pollNextTick()
  2. world/tick/ChunkTickScheduler.java:51
  3. world/tick/ChunkTickScheduler.java:52-54
  4. world/tick/ChunkTickScheduler.java:56

Schedule a tick (1)

ScheduleTick(pos, block, delay, priority = NORMAL)

  1. Compute triggerTick = current time + delay (2)
  2. Assign subTickOrder from the world's global monotonic counter (incremented each call) (3)
  3. Find the chunk scheduler for pos. If the chunk is not loaded: error (4)
  4. If queuedTicks already contains a tick for the same (pos, block) pair: return silently (deduplicated) (5)
  5. Add the new tick to queuedTicks and to tickQueue (6)
  6. Notify WorldTickScheduler to update nextTriggerTickByChunkPos[chunkKey(pos)] to min(stored, triggerTick) (7)
  1. world/WorldAccess.java:37 / world/tick/ChunkTickScheduler.java:60-71
  2. world/WorldAccess.java:37
  3. world/WorldAccess.java:37
  4. world/tick/WorldTickScheduler.java:73-81
  5. world/tick/ChunkTickScheduler.java:61
  6. world/tick/ChunkTickScheduler.java:61-62, 67
  7. world/tick/ChunkTickScheduler.java:68-70

Set block state (1)

SetBlockState(pos, newState, flags)

  1. Ask the chunk to store newState at pos. If the state did not actually change: return false (2)
  2. As part of that chunk write, the following happen in order:
  3. The old state's On state replaced handler is called (3)
  4. The new state's On block added handler is called (4)
  5. If flag NOTIFY_LISTENERS (2) is set: send the change to clients (5)
  6. If flag NOTIFY_NEIGHBORS (1) is set:
  7. Notify neighbors of pos, citing the old block as the source (6)
  8. If the new state has a comparator output: Notify comparators at pos (7)
  9. Return true (8)
  1. world/World.java:189-248 setBlockState()
  2. world/World.java:200-203
  3. world/chunk/WorldChunk.java:263
  4. world/chunk/WorldChunk.java:273
  5. world/World.java:224-228
  6. world/World.java:230-231
  7. world/World.java:232-234
  8. 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)

  1. world/World.java:296-303 updateNeighborsAlways()
  2. 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 direxceptDir: notify the block at pos.offset(dir). (2)

  1. world/World.java:305-329 updateNeighborsExcept()
  2. world/World.java:306-328

Notify one neighbor (1)

NotifyNeighbor(pos, sourceBlock, fromPos)

  1. If running on the client: do nothing (2)
  2. Read the block state at pos (3)
  3. Call that block's neighbor-update handler: (4)
  4. Repeater Repeater neighbor update
  5. Comparator Comparator neighbor update
  6. Wire Wire neighbor update
  7. Redstone torch (floor) Torch neighbor update
  8. Redstone torch (wall) Wall torch neighbor update
  9. Redstone lamp Lamp neighbor update
  10. Any other block: no action within this spec's scope
  1. world/World.java:331-337 updateNeighbor()
  2. world/World.java:332
  3. world/World.java:333
  4. world/World.java:336

Notify comparators (1)

NotifyComparators(pos, sourceBlock)

For each of the 4 horizontal directions [NORTH, EAST, SOUTH, WEST]: (2)

  1. Check the immediate neighbor in that direction. If it is a comparator: call its neighbor update handler (3)
  2. 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)
  1. world/World.java:830-844 updateComparators()
  2. world/World.java:831
  3. world/World.java:834-836
  4. 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)

  1. world/chunk/WorldChunk.java:272-274
  2. world/chunk/WorldChunk.java:273

On state replaced (1)

Called whenever a block state is overwritten. Dispatches on the old block type: (2)

  1. world/chunk/WorldChunk.java:262-266
  2. 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.

  1. Compute outputDir = state.FACING.getOpposite() and outputPos = pos.offset(outputDir) (2)
  2. Notify the block at outputPos, citing this gate as the source (3)
  3. Notify neighbors except one direction at outputPos - all 5 neighbors of the output block, except the one pointing back toward this gate (direction state.FACING) (4)
  1. block/AbstractRedstoneGateBlock.java:169-174 updateTarget()
  2. block/AbstractRedstoneGateBlock.java:170-171
  3. block/AbstractRedstoneGateBlock.java:172
  4. 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.

  1. Read the block state at pos (2)
  2. Get the block's weak power output in direction dir (3)
  3. If the block at pos is a solid opaque block: return the maximum of that weak power and the strong power received at pos (4)
  4. Otherwise: return the weak power directly (5)

A solid opaque block re-emits as weak power any strong power arriving from any face.

  1. world/World.java:680-684 getEmittedRedstonePower()
  2. world/World.java:681
  3. world/World.java:682
  4. world/World.java:683
  5. 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.

  1. world/World.java:645-674 getReceivedStrongRedstonePower()
  2. 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.

  1. world/World.java:700-715 getReceivedRedstonePower()
  2. 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.

  1. world/World.java:686-698 isReceivingRedstonePower()
  2. world/World.java:687-696

Is the position emitting power in a direction? (1)

Return true if EmittedPower(pos, dir) > 0. (2)

  1. world/World.java:676-678 isEmittingRedstonePower()
  2. world/World.java:677

Weak power output (1)

WeakPower(state, pos, dir) - how much weak power this block emits in direction dir.

  1. 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.

  1. BlockState.getStrongRedstonePower - dispatched by block type; see individual block sections
  2. block/RedstoneTorchBlock.java:86-88
  3. 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)

FACING points toward the input block. The output block sits at pos.offset(FACING.getOpposite()). When placed, FACING = playerFacing.getOpposite().


Repeater weak power (1)

  1. If the repeater is not powered: return 0 (2)
  2. If dir == FACING (the querying block is on the output side): return 15 (3)
  3. Otherwise: return 0 (4)
  1. block/AbstractRedstoneGateBlock.java:59-65 getWeakRedstonePower()
  2. block/AbstractRedstoneGateBlock.java:60-61
  3. block/AbstractRedstoneGateBlock.java:63
  4. block/AbstractRedstoneGateBlock.java:63

Repeater front input power (1)

  1. Get the emitted power of the block directly in front (pos.offset(FACING)), measured in the FACING direction (2)
  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 POWER state value (3)
  3. Return the result (4)
  1. block/AbstractRedstoneGateBlock.java:107-117 getPower()
  2. block/AbstractRedstoneGateBlock.java:108-110
  3. block/AbstractRedstoneGateBlock.java:111-116
  4. 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)

  1. block/AbstractRedstoneGateBlock.java:119-124 getMaxInputLevelSides()
  2. block/AbstractRedstoneGateBlock.java:120-122
  3. block/AbstractRedstoneGateBlock.java:123

Repeater side input level (1)

For repeaters, only another repeater or comparator on the side can contribute a locking signal.

  1. Read the block state at sidePos (2)
  2. If that block is not a repeater or comparator: return 0 (3)
  3. Return the strong power output of that block in sideDir (4)
  1. block/AbstractRedstoneGateBlock.java:126-137 getInputLevel() (repeater variant, isValidInput = isRedstoneGate)
  2. block/AbstractRedstoneGateBlock.java:127
  3. block/AbstractRedstoneGateBlock.java:128, 134-136
  4. block/AbstractRedstoneGateBlock.java:132

Repeater locked check (1)

Return true if repeater side input power > 0. (2)

  1. block/RepeaterBlock.java:60-62 isLocked()
  2. block/RepeaterBlock.java:61

Does the repeater face a misaligned gate? (1)

  1. The output block sits at pos.offset(FACING.getOpposite()) (2)
  2. Return true if that block is a repeater or comparator and its FACING is not pointing back toward this repeater (3)
  1. block/AbstractRedstoneGateBlock.java:188-192 isTargetNotAligned()
  2. block/AbstractRedstoneGateBlock.java:189-190
  3. block/AbstractRedstoneGateBlock.java:191

Repeater neighbor update (1)

  1. If the repeater has no floor to stand on: drop as an item, remove the block, notify all 6 neighbors, and stop (2)
  2. Otherwise: check and schedule a state change (3)
  1. block/AbstractRedstoneGateBlock.java:68-80 neighborUpdate()
  2. block/AbstractRedstoneGateBlock.java:71-79
  3. block/AbstractRedstoneGateBlock.java:70

Repeater check and schedule (1)

  1. If the repeater is locked: do nothing (2)
  2. If the current POWERED state already matches whether front input power > 0, or a tick for this repeater is already being processed this game tick: do nothing (3)
  3. Determine scheduling priority: (4)
  4. If the output target is misaligned: EXTREMELY_HIGH (5)
  5. Else if currently powered: VERY_HIGH (6)
  6. Otherwise: HIGH (7)
  7. Schedule a tick for this repeater at DELAY × 2 game ticks from now with that priority (8)
  1. block/AbstractRedstoneGateBlock.java:82-97 updatePowered()
  2. block/AbstractRedstoneGateBlock.java:83
  3. block/AbstractRedstoneGateBlock.java:84-86
  4. block/AbstractRedstoneGateBlock.java:87-93
  5. block/AbstractRedstoneGateBlock.java:88-89
  6. block/AbstractRedstoneGateBlock.java:90-91
  7. block/AbstractRedstoneGateBlock.java:87
  8. block/AbstractRedstoneGateBlock.java:94

Repeater scheduled tick (1)

  1. If the repeater is locked: do nothing (2)
  2. Read current POWERED state and whether front input power > 0 (call it hasPower) (3)
  3. If powered and hasPower is false: set block state to POWERED=false, flags 2 (4)
  4. If not powered: (5)
  5. Set block state to POWERED=true, flags 2 (6)
  6. If hasPower is also false (input disappeared before this tick fired): schedule a tick at DELAY × 2 ticks with priority VERY_HIGH to turn off again (7)
  1. block/AbstractRedstoneGateBlock.java:38-51 scheduledTick()
  2. block/AbstractRedstoneGateBlock.java:39
  3. block/AbstractRedstoneGateBlock.java:40-41
  4. block/AbstractRedstoneGateBlock.java:42-43
  5. block/AbstractRedstoneGateBlock.java:44
  6. block/AbstractRedstoneGateBlock.java:45
  7. block/AbstractRedstoneGateBlock.java:46-48

Repeater on block added (1)

Notify the output side. (2)

  1. block/AbstractRedstoneGateBlock.java:157-159 onBlockAdded()
  2. block/AbstractRedstoneGateBlock.java:158

Repeater on state replaced (1)

  1. If the new block is the same type (e.g., a POWERED or DELAY change): do nothing - the output was already notified via on block added (2)
  2. Otherwise: notify the output side using the old state (3)
  1. block/AbstractRedstoneGateBlock.java:162-167 onStateReplaced()
  2. block/AbstractRedstoneGateBlock.java:163
  3. 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)

  1. If the comparator is not powered: return 0 (2)
  2. If dir == FACING (the querying block is on the output side): return ComparatorBlockEntity(pos).outputSignal (3)
  3. Otherwise: return 0 (4)
  1. block/AbstractRedstoneGateBlock.java:59-65 / block/ComparatorBlock.java:41-44 getWeakRedstonePower() / getOutputLevel()
  2. block/AbstractRedstoneGateBlock.java:60-61
  3. block/AbstractRedstoneGateBlock.java:63 / block/ComparatorBlock.java:43
  4. block/AbstractRedstoneGateBlock.java:63

Comparator front input power (1)

  1. Start with the emitted power of the block directly in front (pos.offset(FACING)), measured in the FACING direction (2)
  2. Check the block directly in front: (3)
  3. If it outputs a comparator signal (e.g., a container): use that signal as the power value (4)
  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 behindPos facing FACING (only counts if exactly one frame is present) (6)
    • Check if the block at behindPos outputs a comparator signal (7)
    • Use the larger of the two (if either exists) (8)
  5. Return the resulting power value (9)
  1. block/ComparatorBlock.java:72-92 getPower()
  2. block/ComparatorBlock.java:73
  3. block/ComparatorBlock.java:74-76
  4. block/ComparatorBlock.java:77-78
  5. block/ComparatorBlock.java:79-88
  6. block/ComparatorBlock.java:82
  7. block/ComparatorBlock.java:84
  8. block/ComparatorBlock.java:83-87
  9. block/ComparatorBlock.java:91

Comparator side input power (1)

Check the two perpendicular neighbors (left and right of FACING). For comparators, any block that emits redstone power counts as a valid side input - not just repeaters and comparators. (2)

Return the maximum of comparator side input level for each side. (3)

  1. block/AbstractRedstoneGateBlock.java:119-124 getMaxInputLevelSides() (comparator variant)
  2. block/AbstractRedstoneGateBlock.java:120-122
  3. block/AbstractRedstoneGateBlock.java:123

Comparator side input level (1)

  1. If the side block does not emit redstone power: return 0 (2)
  2. If it is a redstone block: return 15 (3)
  3. If it is wire: return the wire's POWER state value (4)
  4. Otherwise: return its strong power output in sideDir (5)
  1. block/AbstractRedstoneGateBlock.java:126-137 getInputLevel() (comparator variant, isValidInput = emitsRedstonePower)
  2. block/AbstractRedstoneGateBlock.java:128, 134-136
  3. block/AbstractRedstoneGateBlock.java:129-130
  4. block/AbstractRedstoneGateBlock.java:132
  5. block/AbstractRedstoneGateBlock.java:132

Comparator output signal (1)

  1. Get front input power. If it is 0: return 0 (2)
  2. Get side input power (3)
  3. If side > front: return 0 (4)
  4. If mode is SUBTRACT: return front − side. If mode is COMPARE: return front (5)
  1. block/ComparatorBlock.java:46-58 calculateOutputSignal()
  2. block/ComparatorBlock.java:47-49
  3. block/ComparatorBlock.java:51
  4. block/ComparatorBlock.java:52-53
  5. block/ComparatorBlock.java:55

Does the comparator have power? (1)

  1. Get front input power. If 0: return false (2)
  2. Get side input power (3)
  3. Return true if front > side, or if front == side and mode is COMPARE (4)
  1. block/ComparatorBlock.java:61-69 hasPower()
  2. block/ComparatorBlock.java:62-65
  3. block/ComparatorBlock.java:67
  4. block/ComparatorBlock.java:68

Comparator neighbor update (1)

  1. If a tick for this comparator is already being processed this game tick: do nothing (2)
  2. Compute the new output signal and read the old one from the block entity (0 if absent) (3)
  3. If the output signal hasn't changed and the POWERED state already matches whether it has power: do nothing (4)
  4. Otherwise schedule a tick at 2 game ticks from now: (5)
  5. Priority HIGH if the output target is misaligned (6)
  6. Priority NORMAL otherwise (7)
  1. block/ComparatorBlock.java:119-129 updatePowered()
  2. block/ComparatorBlock.java:120
  3. block/ComparatorBlock.java:121-123
  4. block/ComparatorBlock.java:124
  5. block/ComparatorBlock.java:126
  6. block/ComparatorBlock.java:125
  7. block/ComparatorBlock.java:125

Comparator scheduled tick (1)

  1. Compute the new output signal (2)
  2. Read the old outputSignal from the block entity (0 if absent) (3)
  3. Write the new value to the block entity (4)
  4. If the output signal didn't change and mode is not COMPARE: stop (no state update needed) (5)
  5. Check whether the comparator has power (6)
  6. If currently POWERED but no longer has power: set block state to POWERED=false, flags 2 (7)
  7. If currently not POWERED but now has power: set block state to POWERED=true, flags 2 (8)
  8. Notify the output side (9)
  1. block/ComparatorBlock.java:154-156 update() at 131-151
  2. block/ComparatorBlock.java:132
  3. block/ComparatorBlock.java:133-136
  4. block/ComparatorBlock.java:137
  5. block/ComparatorBlock.java:140
  6. block/ComparatorBlock.java:141
  7. block/ComparatorBlock.java:143-144
  8. block/ComparatorBlock.java:145-146
  9. 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)

  1. If wiresGivePower is false, or dir == DOWN: return 0 (2)
  2. If POWER == 0: return 0 (3)
  3. If dir == UP, or the wire is connected toward the querying block (connection in direction dir.getOpposite() is SIDE or UP): return POWER (4)
  4. Otherwise: return 0 (5)
  1. block/RedstoneWireBlock.java:404-418 getWeakRedstonePower()
  2. block/RedstoneWireBlock.java:405
  3. block/RedstoneWireBlock.java:407-408
  4. block/RedstoneWireBlock.java:410-413
  5. block/RedstoneWireBlock.java:410-413

Wire strong power (1)

  1. If wiresGivePower is false: return 0 (2)
  2. Otherwise: return wire weak power (3)
  1. block/RedstoneWireBlock.java:399-401 getStrongRedstonePower()
  2. block/RedstoneWireBlock.java:400
  3. block/RedstoneWireBlock.java:400

Wire received power (1)

  1. Temporarily set wiresGivePower = false (2)
  2. Get weak power received at pos - finds power from all non-wire sources on all 6 faces. Call this direct (3)
  3. Restore wiresGivePower = true (4)
  4. If direct < 15, also check adjacent wires: (5)
  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 pos is 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)
  6. Track the maximum wire POWER seen; call this adjacent (10)
  7. Return max(direct, adjacent − 1) (11)
  1. block/RedstoneWireBlock.java:307-327 getReceivedRedstonePower()
  2. block/RedstoneWireBlock.java:308
  3. block/RedstoneWireBlock.java:309
  4. block/RedstoneWireBlock.java:310
  5. block/RedstoneWireBlock.java:312
  6. block/RedstoneWireBlock.java:313
  7. block/RedstoneWireBlock.java:316
  8. block/RedstoneWireBlock.java:318-319
  9. block/RedstoneWireBlock.java:320-321
  10. block/RedstoneWireBlock.java:316-321
  11. block/RedstoneWireBlock.java:326

Wire update (1)

  1. Compute wire received power (2)
  2. If POWER already equals the new value: do nothing (3)
  3. If the block state at pos is still the same: set block state with the new POWER value, flags 2 (4)
  4. Build the set of positions to notify: pos itself plus all 6 adjacent positions (5)
  5. For each of those 7 positions: notify neighbors citing Wire as the source (6)
  1. block/RedstoneWireBlock.java:287-305 update()
  2. block/RedstoneWireBlock.java:288
  3. block/RedstoneWireBlock.java:289
  4. block/RedstoneWireBlock.java:290-292
  5. block/RedstoneWireBlock.java:294-299
  6. block/RedstoneWireBlock.java:301-303

Wire neighbor update (1)

  1. If running on the client: do nothing (2)
  2. If the wire has no surface to sit on: drop as an item, remove the block, and stop (3)
  3. Otherwise: wire update (4)
  1. block/RedstoneWireBlock.java:387-396 neighborUpdate()
  2. block/RedstoneWireBlock.java:388
  3. block/RedstoneWireBlock.java:392-394
  4. block/RedstoneWireBlock.java:390

Wire on block added (1)

  1. If running on the client: do nothing (2)
  2. Wire update (3)
  3. Notify neighbors of pos.up() and pos.down(), citing Wire (4)
  4. Notify wire offset neighbors at pos (5)
  1. block/RedstoneWireBlock.java:344-354 onBlockAdded()
  2. block/RedstoneWireBlock.java:345
  3. block/RedstoneWireBlock.java:346
  4. block/RedstoneWireBlock.java:348-350
  5. block/RedstoneWireBlock.java:352

Wire on state replaced (1)

  1. If the block is moving, or the new block is also wire: do nothing (2)
  2. If running on the client: do nothing (3)
  3. For each of the 6 directions: notify neighbors of pos.offset(dir), citing Wire (4)
  4. Wire update using the old state (5)
  5. Notify wire offset neighbors at pos (6)
  1. block/RedstoneWireBlock.java:357-369 onStateReplaced()
  2. block/RedstoneWireBlock.java:358
  3. block/RedstoneWireBlock.java:360
  4. block/RedstoneWireBlock.java:361-363
  5. block/RedstoneWireBlock.java:365
  6. 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)
  1. block/RedstoneWireBlock.java:371-384 updateOffsetNeighbors()
  2. block/RedstoneWireBlock.java:372
  3. block/RedstoneWireBlock.java:373-374
  4. block/RedstoneWireBlock.java:376
  5. block/RedstoneWireBlock.java:378-379
  6. 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)

  1. block/RedstoneTorchBlock.java:48-50 getWeakRedstonePower()
  2. 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)

  1. block/RedstoneTorchBlock.java:52-54 shouldUnpower()
  2. 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)

  1. block/RedstoneTorchBlock.java:79-83 neighborUpdate()
  2. block/RedstoneTorchBlock.java:80-82

Torch scheduled tick (1)

  1. Check whether the torch should turn off (2)
  2. Remove any burnout entries older than 60 game ticks from the burnout table (3)
  3. If lit and should turn off: (4)
  4. Set block state to LIT=false, flags 3 (5)
  5. If the torch is now burned out (recording this event): schedule a tick at 160 game ticks, NORMAL - prevents rapid relighting (6)
  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)
  1. block/RedstoneTorchBlock.java:57-76 scheduledTick()
  2. block/RedstoneTorchBlock.java:58
  3. block/RedstoneTorchBlock.java:59-63
  4. block/RedstoneTorchBlock.java:65-66
  5. block/RedstoneTorchBlock.java:67
  6. block/RedstoneTorchBlock.java:68-71
  7. 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.

  1. Look up this position's entry list in the per-world burnout table (create it if absent) (2)
  2. If addNew is true: append an entry with the current game tick (3)
  3. Count entries for this position. If 8 or more: return true (burned out); otherwise false (4)
  1. block/RedstoneTorchBlock.java:110-128 isBurnedOut()
  2. block/RedstoneTorchBlock.java:111
  3. block/RedstoneTorchBlock.java:112-114
  4. 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)

  1. block/RedstoneTorchBlock.java:32-36 onBlockAdded()
  2. 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.)

  1. block/RedstoneTorchBlock.java:39-45 onStateReplaced()
  2. 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 dirFACING, otherwise return 0. (2)

  1. block/WallRedstoneTorchBlock.java:76-78 getWeakRedstonePower()
  2. block/WallRedstoneTorchBlock.java:77

Should the wall torch turn off? (1)

  1. mountDir = FACING.getOpposite() (2)
  2. Return true if the mounting block at pos.offset(mountDir) is emitting power in direction mountDir (3)
  1. block/WallRedstoneTorchBlock.java:70-73 shouldUnpower()
  2. block/WallRedstoneTorchBlock.java:71
  3. 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)

  1. If running on the client: do nothing (2)
  2. Read lit = state.LIT and receiving = is the position receiving any power? (3)
  3. If lit == receiving: do nothing (state is already correct) (4)
  4. If the lamp is lit and signal was lost: schedule a tick at 4 game ticks, priority NORMAL (5)
  5. If the lamp is unlit and signal arrived: set block state to LIT=true, flags 2 (immediate, no delay) (6)
  1. block/RedstoneLampBlock.java:27-38 neighborUpdate()
  2. block/RedstoneLampBlock.java:28
  3. block/RedstoneLampBlock.java:29-30
  4. block/RedstoneLampBlock.java:30
  5. block/RedstoneLampBlock.java:31-32
  6. 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)

  1. block/RedstoneLampBlock.java:41-45 scheduledTick()
  2. 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)

  1. block/ChestBlock.java:351-352 getComparatorOutput()
  2. 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)

  1. block/BarrelBlock.java:103-104 getComparatorOutput()
  2. block/BarrelBlock.java:104

Inventory fill signal (1)

  1. If the inventory is null: return 0 (2)
  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)
  3. Divide the total by the number of slots to get a fill ratio (4)
  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.

  1. screen/ScreenHandler.java:707-725 calculateComparatorOutput()
  2. screen/ScreenHandler.java:708-710
  3. screen/ScreenHandler.java:715-721
  4. screen/ScreenHandler.java:723
  5. screen/ScreenHandler.java:724