import sources from scattered branches
This commit is contained in:
8
content/bulk.typ
Normal file
8
content/bulk.typ
Normal file
@@ -0,0 +1,8 @@
|
||||
= Bulk Transport
|
||||
|
||||
== Tileable Receivers
|
||||
== Interleaved vs Sequential Channels
|
||||
== Chained vs Shared Reference
|
||||
== Broadcast and Multicast
|
||||
== Entity Batching
|
||||
|
||||
7
content/channels.typ
Normal file
7
content/channels.typ
Normal file
@@ -0,0 +1,7 @@
|
||||
= Channels
|
||||
|
||||
== Static Channel Allocation
|
||||
== Dynamic Channel Selectors
|
||||
== Looped Connections
|
||||
=== Ender Pearls
|
||||
=== Secure Connection Requests
|
||||
291
content/core.typ
Normal file
291
content/core.typ
Normal file
@@ -0,0 +1,291 @@
|
||||
#import "/lib.typ": callout, details, example, note, solution, tip, todo
|
||||
|
||||
= Core Mechanics <core>
|
||||
|
||||
== Entity IDs
|
||||
|
||||
Entities are the dynamic movable objects in the game. Minecraft tracks every entity with a unique ID
|
||||
number. Whenever an entity is added to the game world, a global counter is incremented and the new
|
||||
entity is assigned the current value.
|
||||
|
||||
A large number of game objects are tracked in this way, and so creating any one of them will
|
||||
increment that global counter. To reiterate, *this is a global counter, shared for all entities in
|
||||
the game*.
|
||||
|
||||
== Stationary Item Optimization
|
||||
|
||||
Typically, item entities fall to the floor and sit stationary soon after being created. Processing
|
||||
movement for these stationary entities would be wasteful, so an optimization was added in 1.14: if
|
||||
an item is sitting stationary on a block, then only process movement for that item every 4th tick.
|
||||
|
||||
However, it would also be wasteful if every 4th tick processed all items, so instead the items are
|
||||
divided into four staggered groups. Each tick stationary items from only one of the four groups
|
||||
check for movement.
|
||||
|
||||
#callout(kind: "tip", label: "Key Point")[
|
||||
Stationary items only begin to fall when their $"age" + "id"$ is a multiple of $4$.
|
||||
]
|
||||
|
||||
In other words, divide the value by 4 and look at the remainder. If the remainder is 0, the item
|
||||
checks for movement. That condition is written as `... % 4 == 0` in Java and as $... equiv 0 &(mod
|
||||
4)$ in math.
|
||||
|
||||
You can freely add or subtract multiples of 4 to any expression without changing the value $mod 4$.
|
||||
For example, $-1 equiv 3 &(mod 4)$ because $3 = -1 + 4$.
|
||||
|
||||
The optimization is implemented as follows:
|
||||
|
||||
#note[ `onGround` is only ever updated via `move`, and this branch is the *only* place that
|
||||
`ItemEntity.move` is called. `tickCount` is the item's age. ]
|
||||
|
||||
```java
|
||||
public class ItemEntity extends Entity {
|
||||
public void tick() {
|
||||
...
|
||||
if (this.onGround() && !(this.getDeltaMovement().horizontalDistanceSqr() > (double)1.0E-5F) && (this.tickCount + this.getId()) % 4 != 0) {
|
||||
...
|
||||
} else {
|
||||
this.move(MoverType.SELF, this.getDeltaMovement());
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
public class Entity {
|
||||
public void move(final MoverType moverType, Vec3 delta) {
|
||||
...
|
||||
this.setOnGroundWithMovement(this.verticalCollisionBelow, this.horizontalCollision, movement);
|
||||
...
|
||||
}
|
||||
|
||||
public void setOnGroundWithMovement(final boolean onGround, final boolean horizontalCollision, final Vec3 movement) {
|
||||
this.onGround = onGround;
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The effect is that if the item is on a block and has negligible horizontal momentum, it *cannot
|
||||
fall* until the condition is satisfied, even if it's no longer on the ground. It can only fall early
|
||||
if it gains horizontal momentum or is pushed.
|
||||
|
||||
#todo[Falling item animation][Spawn a few items, note their ID and age. Let them settle on a block, then
|
||||
remove that supporting block. There will be a few ticks where the item hovers in place before it
|
||||
falls.]
|
||||
|
||||
== Observable Drop Delay
|
||||
|
||||
We can't control the absolute entity ID a particular item will receive, but we can *compare* ids of
|
||||
multiple entities. By observing the drop delay for a "reference" item, we can predict the behavior
|
||||
of other items.
|
||||
|
||||
#example[
|
||||
For example, suppose we spawn an item $A$, then 2 game ticks later we spawn an item $B$. Assume no
|
||||
other entities spawn between them, so $A$ and $B$ have consecutive IDs. Wait for the items to
|
||||
settle, then remove the supporting blocks. If we observe item $A$ start to fall 1 game tick after
|
||||
the support is removed, we can predict when item $B$ will fall.
|
||||
|
||||
It'll be easier if we orient ourselves around the tick when the support blocks are removed, call
|
||||
that $t=0$. Say the age is $"age"_(A, 0)$ at that time. We have that item $A$ falls at $t=1$.
|
||||
|
||||
$A$ fell when it was $"age"_(A, 1) = "age"_(A, 0) + 1$ old, so we can put all this into a relation
|
||||
that pins down the mod 4 cycle.
|
||||
|
||||
$ "age"_(A, 0) + 1 + "id"_A equiv 0 (mod 4) $
|
||||
|
||||
We want to find the equivalent relation for $B$, where $x$ will be the tick in which $B$ falls.
|
||||
|
||||
$ "age"_(B, 0) + x + "id"_B equiv 0 (mod 4) $
|
||||
|
||||
We know that $A$ has the item ID immediately before $B$, so $"id"_A = "id"_B - 1$.
|
||||
|
||||
We also know that $A$ is 2gt older than $B$, so $"age"_(A, 0) = "age"_(B, 0) + 2$.
|
||||
|
||||
So take the relation for $A$ and substitute in these values for $B$.
|
||||
|
||||
$
|
||||
"age"_(A, 0) + 1 + "id"_A & equiv 0 (mod 4) \
|
||||
"age"_(B, 0) + 2 + 1 + "id"_B - 1 & equiv 0 (mod 4) \
|
||||
"age"_(B, 0) + 2 + "id"_B & equiv 0 (mod 4) \
|
||||
$
|
||||
|
||||
Compare with the relation for $B$ and we see that $x=2$.
|
||||
|
||||
#callout(kind: "tip", label: "Therefore:")[$B$ falls at time $t=2$.]
|
||||
]
|
||||
|
||||
#example[
|
||||
Suppose we spawn four items in order. For simplicity, spawn them all in the same tick so their
|
||||
ages are equal, and assume no other entities spawn.
|
||||
|
||||
1. Spawn items $A$, $B$, $C$, and $D$ in order in the same tick. Let them settle on a block.
|
||||
2. At tick $t=0$, remove the supporting blocks.
|
||||
3. At tick $t=1$, item $B$ begins to fall.
|
||||
|
||||
Based on when item $B$ fell, when should we expect the other items to fall?
|
||||
|
||||
#solution[
|
||||
To simplify the arithmetic, orient everything around $t=0$. So we'll let $"age"_0$ be the
|
||||
items' ages at $t=0$ and add tick offsets from there. All items spawned in the same tick, so
|
||||
they all have the same $"age"_0$.
|
||||
|
||||
Since $B$ falls at $t=1$, the relation for $"id"_B$ must be
|
||||
|
||||
$ "age"_0 + "id"_B + 1 equiv 0 & (mod 4) $
|
||||
|
||||
Item $A$ gets the ID 1 before $B$, so $"id"_A + 1 = "id"_B$.
|
||||
Plug that in to the relation we got for $"id"_B$.
|
||||
|
||||
$ "age"_0 + "id"_A + 1 + 1 equiv 0 & (mod 4) $
|
||||
|
||||
*$A$ must fall at $t=2$.*
|
||||
|
||||
Item $C$ gets the ID 1 after $B$, so $"id"_C - 1= "id"_B$. Plug that in.
|
||||
|
||||
$ "age"_0 + "id"_C - 1 + 1 equiv 0 & (mod 4) $
|
||||
|
||||
*$C$ must fall at $t=0$.*
|
||||
|
||||
Item $D$ gets the ID 2 after $B$, so $"id"_D - 2 = "id"_B$. Plug that in.
|
||||
|
||||
$
|
||||
"age"_0 + "id"_D - 2 + 1 & equiv 0 & (mod 4) \
|
||||
"age"_0 + "id"_D + 3 & equiv 0 & (mod 4)
|
||||
$
|
||||
|
||||
*$D$ must fall at $t=3$.*
|
||||
|
||||
#callout(kind: "tip", label: "Therefore:")[
|
||||
The expected fall times are:
|
||||
|
||||
- $C$ at $t=0$.
|
||||
- $B$ at $t=1$.
|
||||
- $A$ at $t=2$.
|
||||
- $D$ at $t=3$.
|
||||
]
|
||||
]
|
||||
|
||||
#todo[Demonstration]
|
||||
]
|
||||
|
||||
#example[
|
||||
Consider a variation of the previous example, but where the items do not spawn on the same tick.
|
||||
Their ages will be different. Again, assume no other entities spawn.
|
||||
|
||||
1. Spawn item $A$ at $t=-10$.
|
||||
2. Spawn items $B$ and $C$ in order at $t=-8$.
|
||||
3. Spawn item $D$ at $t=-7$. Let them settle on a block.
|
||||
4. At tick $t=0$, remove the supporting blocks.
|
||||
5. At tick $t=1$, item $B$ begins to fall.
|
||||
|
||||
Based on when item $B$ fell, and accounting for the different item ages, when should we expect the
|
||||
other items to fall?
|
||||
|
||||
#solution[
|
||||
We'll need to track the item ages separately this time, but still oriented around $t=0$.
|
||||
|
||||
Let $"age"_(0, B)$ be item $B$'s age at $t=0$.
|
||||
|
||||
We know item $B$ fell at $t=1$, so we have the relation
|
||||
$ "age"_(0, B) + "id"_B + 1 equiv 0 & (mod 4) $
|
||||
|
||||
#todo[Add "gt" to the glossary]
|
||||
|
||||
Item $A$ gets the ID 1 before $B$, so $"id"_A + 1 = "id"_B$.
|
||||
It spawned 2gt earlier, so $"age"_(0, A) - 2= "age"_(0, B)$. Plug those in.
|
||||
|
||||
$ "age"_(0, A) - 2 + "id"_A + 1 + 1 equiv 0 & (mod 4) $
|
||||
|
||||
*$A$ must fall at $t=0$.*
|
||||
|
||||
Item $C$ gets the ID 1 after $B$, so $"id"_C - 1 = "id"_B$.
|
||||
It spawned in the same tick, so $"age"_(0, C) = "age"_(0, B)$. Plug those in.
|
||||
|
||||
$ "age"_(0, C) + "id"_C - 1 + 1 equiv 0 & (mod 4) $
|
||||
|
||||
*$C$ must fall at $t=0$.*
|
||||
|
||||
Item $D$ gets the ID 2 after $B$, so $"id"_D - 2 = "id"_B$.
|
||||
It spawned 1gt later, so $"age"_(0, D) + 1 = "age"_(0, B)$. Plug those in.
|
||||
|
||||
$ "age"_(0, D) + 1 + "id"_D - 2 + 1 equiv 0 & (mod 4) $
|
||||
|
||||
*$D$ must fall at $t=0$.*
|
||||
|
||||
#callout(kind: "tip", label: "Therefore:")[
|
||||
The expected fall times are:
|
||||
|
||||
- $A$ at $t=0$.
|
||||
- $C$ at $t=0$.
|
||||
- $D$ at $t=0$.
|
||||
- $B$ at $t=1$.
|
||||
]
|
||||
]
|
||||
|
||||
#todo[Demonstration]
|
||||
]
|
||||
|
||||
#example[
|
||||
Now consider what it means if the pattern is broken. Keep the item ages the same for simplicity,
|
||||
but this time do *not* assume that no other entities spawn.
|
||||
|
||||
1. Spawn items $A$, $B$, and $C$ in order in the same tick. Let them settle on a block.
|
||||
2. At tick $t=0$, remove the supporting blocks. Item $C$ begins to fall.
|
||||
3. At tick $t=1$, item $A$ begins to fall.
|
||||
4. At tick $t=2$, item $B$ begins to fall.
|
||||
|
||||
What number of entities must have spawned between items $A$ and $B$? Between $B$ and $C$?
|
||||
|
||||
#solution[
|
||||
Following the same conventions as before, where $x$ is the number of entities between $A$ and
|
||||
$B$ and $y$ is the number of entities between $B$ and $C$.
|
||||
|
||||
The ids of $A$ and $B$ are related by
|
||||
|
||||
$ "id"_A + 1 + x = "id"_B $
|
||||
|
||||
And the observed drop delays satisfy
|
||||
|
||||
$ "age"_0 + "id"_A + 1 equiv 0 & (mod 4) $
|
||||
$ "age"_0 + "id"_B + 2 equiv 0 & (mod 4) $
|
||||
|
||||
Substitute $"id"_B$ and solve for $x$.
|
||||
|
||||
$
|
||||
"age"_0 + "id"_A + 1 + x + 2 & equiv "age"_0 + "id"_A + 1 & (mod 4) \
|
||||
x + 2 & equiv 0 & (mod 4) \
|
||||
x & equiv 2 & (mod 4) \
|
||||
$
|
||||
|
||||
*So the number of entities between $A$ and $B$ is $2 &(mod 4)$.* (For example, 2, 6, 10, ...)
|
||||
|
||||
The ids of $B$ and $C$ are related by
|
||||
|
||||
$ "id"_B + 1 + y = "id"_C $
|
||||
|
||||
And the observed drop delays satisfy
|
||||
|
||||
$ "age"_0 + "id"_B + 2 equiv 0 & (mod 4) $
|
||||
$ "age"_0 + "id"_C + 0 equiv 0 & (mod 4) $
|
||||
|
||||
Substitute $"id"_C$ and solve for $y$.
|
||||
|
||||
$
|
||||
"age"_0 + "id"_B + 1 + y + 0 & equiv "age"_0 + "id"_B + 2 & (mod 4) \
|
||||
y - 1 & equiv 0 & (mod 4) \
|
||||
y & equiv 1 & (mod 4) \
|
||||
$
|
||||
|
||||
*So the number of entities between $B$ and $C$ is $1 &(mod 4)$.* (For example, 1, 5, 9, ...)
|
||||
|
||||
#callout(kind: "tip", label: "Therefore:")[
|
||||
The number of spawned entities must be
|
||||
|
||||
- $2 &(mod 4)$ between $A$ and $B$.
|
||||
- $1 &(mod 4)$ between $B$ and $C$.
|
||||
]
|
||||
]
|
||||
|
||||
#todo[Demonstration]
|
||||
]
|
||||
8
content/data-protocols.typ
Normal file
8
content/data-protocols.typ
Normal file
@@ -0,0 +1,8 @@
|
||||
= Data Protocols
|
||||
|
||||
== Mod 4 Binary
|
||||
== Mod 2 Binary
|
||||
== Mod 4 Quaternary
|
||||
== Transceivers
|
||||
== Logical Operations
|
||||
== Transport Protocol Catalog
|
||||
8
content/design.typ
Normal file
8
content/design.typ
Normal file
@@ -0,0 +1,8 @@
|
||||
= Design Tips
|
||||
|
||||
== Synchronization
|
||||
=== Daylight Detector
|
||||
=== Geyser
|
||||
=== Synchronization Protocols
|
||||
== Settling time
|
||||
== Horizontal vs Vertical Arrangement
|
||||
17
content/index.typ
Normal file
17
content/index.typ
Normal file
@@ -0,0 +1,17 @@
|
||||
#import "/lib.typ": callout, diorama, warn, world
|
||||
|
||||
#title()
|
||||
|
||||
Everything I know about Entity ID Wireless Redstone (EID Wireless) for Minecraft Java Edition 1.14+.
|
||||
|
||||
#warn[
|
||||
In versions 1.14 - 26.1, only multiplayer servers are supported.
|
||||
|
||||
Singleplayer is supported in 26.2+. See @singleplayer for details.
|
||||
|
||||
It works on most (but not all) versions of Paper servers. See @paper for details.
|
||||
|
||||
It does not work with any version of Bedrock Edition.
|
||||
]
|
||||
|
||||
#outline(title: [Outline], depth: 3)
|
||||
118
content/interference-causes.typ
Normal file
118
content/interference-causes.typ
Normal file
@@ -0,0 +1,118 @@
|
||||
#import "/lib.typ": note, tip, todo
|
||||
|
||||
= Causes of Interference
|
||||
|
||||
We need precise control over the entity age and ID for this to work, so sources of interference are
|
||||
generally unexpected entity spawns that mess up the ID.
|
||||
|
||||
== Other Entities
|
||||
|
||||
Recall that *any* entity spawning will increment the ID counter, and the full list of entities is
|
||||
surprising. An exhaustive list is available at #link(
|
||||
"https://minecraft.wiki/w/Entity#Types_of_entities",
|
||||
)[The Minecraft Wiki].
|
||||
|
||||
In general, the solution is to spawn our item entities at nearly the same time, so there is no
|
||||
chance for other entities to spawn in-between. For example, by spawning all items with droppers in
|
||||
the same game tick, all the items are created in the same tick phase, so player inputs and world
|
||||
events cannot affect it. See @timing for details.
|
||||
|
||||
== Singleplayer (before 26.2) <singleplayer>
|
||||
|
||||
In singleplayer worlds, there are separate execution threads for the renderer and game world. The
|
||||
game world ("server thread") contains the item entities we care about for the purposes of EID
|
||||
Wireless, as these are the entities we can detect with redstone. However, the renderer ("client
|
||||
thread") *also* tracks some entities.
|
||||
|
||||
In versions before 26.2, there was a bug that caused the client thread and server thread to use the
|
||||
*same* global ID counter. Therefore every entity might increment the counter twice: once when it
|
||||
spawns in the server, and once when the client begins to track it. Because this depends on the
|
||||
position and orientation of the player, and also on the unpredictable scheduling of the two
|
||||
execution threads, it is impossible to truly compensate for these effects.
|
||||
|
||||
The solution is to either use a dedicated minecraft server, so that the client and servers run in
|
||||
separate processes, or install a mod which patches the game to use a separate counter for the
|
||||
client.
|
||||
|
||||
#todo[Singleplayer patch links][Grab links for these mods and figure out exactly which versions
|
||||
they're good for.]
|
||||
|
||||
== Paper Servers <paper>
|
||||
|
||||
#todo[Paper compatibility list][Figure out which versions of Paper broke the thing.]
|
||||
|
||||
Most recent versions of Paper server are compatible with EID Wireless. However, there are old
|
||||
versions of Spigot which include a *different* stationary item optimization which corrupts this. A
|
||||
few versions of Paper erroneously included this old optimization (on top of the mod 4 optimization)
|
||||
which breaks EID Wireless.
|
||||
|
||||
#todo[Broken Paper/Spigot code][Show the patch with the busted optimization.]
|
||||
|
||||
== Unloaded Chunks
|
||||
|
||||
When a chunk unloads and reloads, all entities contained within it are destroyed and recreated with
|
||||
new entity IDs. Therefore any information encoded in the ID group offsets is destroyed.
|
||||
|
||||
#tip[
|
||||
When a chunk containing an EID Wireless receiver is reloaded, every in-progress transmission
|
||||
*MUST* be discarded.
|
||||
]
|
||||
|
||||
The best thing to do is to use a reload detector and lock the receiver output for one full cycle
|
||||
after a reload.
|
||||
|
||||
#todo[Reload detector designs][frost walker, sculk sensor, ???]
|
||||
|
||||
== Lazy Chunks
|
||||
|
||||
Entities in lazy chunks do not age or move, and their IDs are preserved. However, the redstone
|
||||
circuitry will continue to function. If a chunk becomes lazy while a transmission is being
|
||||
processed, the detection circuits will not observe the right drop delays and so the transmission is
|
||||
corrupted.
|
||||
|
||||
#tip[
|
||||
When a chunk containing an EID Wireless receiver becomes lazy, every in-progress transmission
|
||||
*MUST* be discarded.
|
||||
]
|
||||
|
||||
Further, depending on the receiver design, there is a small chance the items become clipped into
|
||||
blocks. When the chunk becomes entity-processing again, the clipped items will fly out of the
|
||||
machine instead of being recycled.
|
||||
|
||||
The best thing to do is to use a chunkloader to prevent the receiver ever being lazy-loaded. If
|
||||
chunkloaders cannot be used, a lazy-chunk detector can be used to lock the outputs while the chunk
|
||||
is lazy-loaded and for one full cycle after it becomes entity-processing.
|
||||
|
||||
#todo[Lazy chunk detector designs][falling entity, ???]
|
||||
|
||||
#todo[think about lazy conditions by cases]
|
||||
|
||||
|
||||
#todo[wip comment on discord][
|
||||
The problem with unloaded chunks is that when the chunk reloads, all items are recreated with new
|
||||
entity ids and the transmission information is destroyed. There is no hope for recovery, so the
|
||||
only solution is to discard the first cycle.
|
||||
|
||||
The problem with lazy chunks is that it's impossible (or at least very hard) to synchronize entity
|
||||
movement. The redstone, droppers, and hoppers all continue to tick in lazy chunks. If you have
|
||||
multiple items in each slice, then every cycle in lazy chunks spaws an item. When you the chunk
|
||||
ticks again, the hopper can onl pick up one of the items, and the rest sit there. You can't
|
||||
control when in the cycle the chunk ticks again either, so it may tick with the trapdoor already
|
||||
open, or just about to close.
|
||||
|
||||
Quite often, things will be desynchronized such that the trapdoor closes clipped into an item
|
||||
while the hopper is on cooldown. In that case, the collision flings the item out of the
|
||||
measurement chamber. If you only have one item, then the hopper can never be on cooldown while the
|
||||
item would collide with the trapdoor, so it always gets recycled and can never be flung out.
|
||||
|
||||
However because you can't control the timings, you can't guarantee the mod 4 optimization was
|
||||
actually applied or if the items are just falling through the already-open trapdoor. So you still
|
||||
have to discard the message.
|
||||
|
||||
If you have a lazy chunk detector, you can stop the clock while in lazy chunks, then simply resume
|
||||
the clock on the next cycle after ticking again.
|
||||
|
||||
But since you also need to lock the output for reloaded chunks, it's just easier to do that every
|
||||
time.
|
||||
]
|
||||
|
||||
217
content/interference-fixes.typ
Normal file
217
content/interference-fixes.typ
Normal file
@@ -0,0 +1,217 @@
|
||||
#import "/lib.typ": diorama, example, todo, world
|
||||
|
||||
= Preventing Interference <timing>
|
||||
|
||||
The protocols as described are very sensitive to the exact timing that the EID counter is
|
||||
incremented. The fundamental cause for all types of interference is that we spawn a reference and
|
||||
measurement item with too much delay, and some unexpected entity (or entities) spawn in-between. To
|
||||
mitigate interference, then, we need that window to be *as short as possible* while still allowing
|
||||
us to spawn transmission entities in that window.
|
||||
|
||||
== Tilesets
|
||||
|
||||
The fundamental principle here is to use tile-tick priority (TTP) to set a global order. See
|
||||
Charlie's great video on TTP for details. #todo[link video]
|
||||
|
||||
#todo[fix tileset naming. the tileset is the *structure*. the particular sequence is just one instance of that tileset]
|
||||
|
||||
A *tileset* is a sequence of redstone components which all update in the tile tick phase. Components
|
||||
later in the sequence have greater significance. To make the significance match our left-to-right
|
||||
reading convention, we diagram them so the signal flows right-to-left. For example:
|
||||
|
||||
|
||||
#diorama(
|
||||
theta: 160,
|
||||
phi: 20,
|
||||
autoplay: true,
|
||||
loop: true,
|
||||
world(
|
||||
"
|
||||
p 0 -1 0 smooth_stone_slab type=top
|
||||
p 1 -1 0 smooth_stone_slab type=top
|
||||
p 2 -1 0 smooth_stone_slab type=top
|
||||
p 3 -1 0 smooth_stone_slab type=top
|
||||
p 4 -1 0 smooth_stone_slab type=top
|
||||
|
||||
p 0 0 0 repeater facing=west powered=false locked=false delay=1
|
||||
p 1 0 0 observer facing=west powered=false
|
||||
p 2 0 0 repeater facing=west powered=false locked=false delay=2
|
||||
p 3 0 0 comparator facing=west powered=false mode=compare
|
||||
p 4 0 0 repeater facing=west powered=false locked=false delay=1
|
||||
|
||||
t 2
|
||||
p 0 0 0 powered=true
|
||||
t 4
|
||||
p 0 0 0 powered=false
|
||||
p 1 0 0 powered=true
|
||||
t 6
|
||||
p 1 0 0 powered=false
|
||||
t 8
|
||||
p 2 0 0 powered=true
|
||||
t 10
|
||||
p 3 0 0 powered=true
|
||||
t 12
|
||||
p 2 0 0 powered=false
|
||||
p 4 0 0 powered=true
|
||||
t 14
|
||||
p 3 0 0 powered=false
|
||||
t 16
|
||||
p 4 0 0 powered=false
|
||||
|
||||
t 20
|
||||
p 0 0 0
|
||||
",
|
||||
),
|
||||
)
|
||||
|
||||
This section will cover how to read and construct tilesets that enforce a *global* ordering for use
|
||||
in wireless redstone.
|
||||
|
||||
=== The Tile Tick Priority Queue
|
||||
|
||||
The game schedules tile updates through a priority queue; different components have different
|
||||
priority, so each stage of the tileset iteratively refines the global order. Across the full
|
||||
tileset, we can enforce an arbitrary global ordering.
|
||||
|
||||
#todo[the priority table][
|
||||
call out here that basic comparators and all other components, so from here on out we're just
|
||||
going to use comparators for simplicity.
|
||||
]
|
||||
|
||||
Components with different priority always update in priority order, but components with equal
|
||||
priority update in scheduled order.
|
||||
|
||||
#todo[rephrase or remove][
|
||||
The core principal is that repeaters have higher priority than other components. For example, if a
|
||||
repeater and comparator are scheduled to activate in the same tick, the repeater always (with one
|
||||
exception, if the comparator faces into a diode and the repeater does not) activates before the
|
||||
comparator does. By choosing a regular building pattern, we can avoid this exception entirely.
|
||||
|
||||
// Among repeaters, they activate in the order in which they were scheduled. And among
|
||||
// comparators, they activate in the order in which they are scheduled. But every repeater
|
||||
// activates before any comparator (aside from that one exception). This gives us a *stable sort*
|
||||
// which we can use to globally define update order with arbitrary precision.
|
||||
|
||||
]
|
||||
|
||||
#example[
|
||||
#todo[this explanation is so janky]
|
||||
|
||||
Say these three tilesets are started in the same gametick, `t=0`.
|
||||
|
||||
```
|
||||
<------
|
||||
1 rep cmp ab
|
||||
2 cmp rep cd
|
||||
3 2rep e
|
||||
<------
|
||||
```
|
||||
|
||||
Once `b`, `d`, and `e` are scheduled, the priority queue looks like this:
|
||||
|
||||
```
|
||||
t=2 [d] [b]
|
||||
t=4 [e]
|
||||
```
|
||||
|
||||
Now at `t=2`, we process the queue in order.
|
||||
|
||||
- `d` activates and schedules `c`.
|
||||
- `b` activates and schedules `a`.
|
||||
|
||||
```
|
||||
t=4 [e a] [c]
|
||||
```
|
||||
|
||||
Now at `t=4`, the end of the tileset, the lanes will always update in order `3 1 2`
|
||||
|
||||
]
|
||||
|
||||
So the full picture involves arbitrary components and priorities. As long as all the tilesets have
|
||||
the same total delay and end at the same time, we can determine a global ordering. However the
|
||||
general picture is hard to reason about, we basically have to simulate the priority queue to make
|
||||
predictions. If we restrict the design of the tilesets a bit, there are two simplifications we could
|
||||
take to make things easier to reason about.
|
||||
|
||||
=== Permutation Tilesets
|
||||
|
||||
#todo[describe the mixed delay permutation tilesets and the alphabetization procedure]
|
||||
|
||||
=== Binary
|
||||
|
||||
The simplest tilesets are made entirely of comparators and 2gt repeaters (alternatively: observers
|
||||
and 2gt repeaters). Repeaters activate before comparators, so if we think of it like sorting words
|
||||
alphabetically, we can identify repeaters with "A" and comparators with "B".
|
||||
|
||||
So, suppose we have a "binary tileset" that is 2gt long. There are two options, A and B. If we
|
||||
alphabetize these, we see the A (repeater) always executes before the B (comparator). With such a
|
||||
short tileset, that seems trivial; things get more interesting as we add more elements.
|
||||
|
||||
```
|
||||
<--
|
||||
cmp (B)
|
||||
rep (A)
|
||||
```
|
||||
|
||||
Now let's use 2 components for a 4gt tileset. There are now four options, AA, AB, BA, BB. We can
|
||||
alphabetize these and see the order.
|
||||
|
||||
```
|
||||
<------
|
||||
cmp cmp (BB)
|
||||
cmp rep (BA)
|
||||
rep cmp (AB)
|
||||
rep rep (AA)
|
||||
```
|
||||
|
||||
To break this down: the bottom two (AA, AB) end in repeaters, so they must come first. Among those,
|
||||
(AA) comes first. Among the top two (BA, BB), (BA) comes first. It's standard alphabetizing. Just as
|
||||
the order of the alphabet creates an ordering over all words, the ordering of the comparator and
|
||||
repeater creates an ordering of all tilesets.
|
||||
|
||||
The 6gt tileset. There are now eight options.
|
||||
|
||||
```
|
||||
<---------
|
||||
...
|
||||
```
|
||||
|
||||
As the tilesets get larger, it's less useful to lay out the entire tileset and more useful to find
|
||||
the next and previous lanes.
|
||||
|
||||
For example, take this 7-diode tileset.
|
||||
|
||||
```
|
||||
cmp rep rep cmp rep cmp cmp
|
||||
<--------------------------
|
||||
B A A B A B B
|
||||
```
|
||||
|
||||
We can easily find the next lane by thinking of this not as a *word* but as a *number*. We have two
|
||||
options, and one is greater than the other.
|
||||
|
||||
```
|
||||
cmp rep rep cmp rep cmp cmp
|
||||
<--------------------------
|
||||
B A A B A B B
|
||||
1 0 0 1 0 1 1
|
||||
```
|
||||
|
||||
We can think of this tileset as a 7-bit binary number, in this case the value 75. We can find the
|
||||
next lane by simply incrementing by one.
|
||||
|
||||
```
|
||||
1 0 0 1 0 1 1 (75)
|
||||
+ 1
|
||||
1 0 0 1 1 0 0 (76)
|
||||
<--------------------------
|
||||
cmp rep rep cmp cmp rep rep
|
||||
```
|
||||
|
||||
=== Lexicographic
|
||||
|
||||
=== Jamming
|
||||
|
||||
== Block Event Delay
|
||||
|
||||
== Single-Priority Loops
|
||||
12
content/network.typ
Normal file
12
content/network.typ
Normal file
@@ -0,0 +1,12 @@
|
||||
#import "/lib.typ": todo
|
||||
|
||||
= Network Protocols
|
||||
|
||||
#todo["Network Protocol" is inaccurate]
|
||||
|
||||
== Collision Prevention
|
||||
=== Checkbit
|
||||
=== Queue
|
||||
== Load Balancing
|
||||
== Private Channels
|
||||
== Self Syncing Protocol
|
||||
Reference in New Issue
Block a user