wip content
This commit is contained in:
@@ -1,70 +0,0 @@
|
|||||||
#import "/lib.typ": note, tip, todo
|
|
||||||
|
|
||||||
= Core Mechanics
|
|
||||||
|
|
||||||
== 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 check for gravity every 4 ticks.
|
|
||||||
|
|
||||||
Here is the relevant code path for the optimization.
|
|
||||||
|
|
||||||
#note[ `onGround` is only ever updated from `move()`, and this branch is the *only* place that
|
|
||||||
`move()` is called. ]
|
|
||||||
|
|
||||||
```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;
|
|
||||||
...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
== Observable Drop Delay
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
#tip[ You can rewrite the condition as `(age % 4 == (-id) % 4)` ]
|
|
||||||
|
|
||||||
#todo[ An 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. ]
|
|
||||||
|
|
||||||
For simplicity, suppose we spawn all our items on the same tick. Then `this.tickCount` will be the
|
|
||||||
same for all of them, and we only need to consider the ID.
|
|
||||||
|
|
||||||
The result of this is that if you spawn several item entities, allow them to settle on a block, then
|
|
||||||
remove that block
|
|
||||||
133
content/core.typ
Normal file
133
content/core.typ
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#import "/lib.typ": callout, 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 every 4th tick.
|
||||||
|
|
||||||
|
However, it would also be wasteful if every 4th tick processed _every_ item, so instead the items
|
||||||
|
are divided into four groups. Each tick stationary items from only one of the four groups check for
|
||||||
|
movement.
|
||||||
|
|
||||||
|
#callout(kind: "tip", label: "Key Point:")[
|
||||||
|
Stationary items can only begin to fall when their $"age" + "id"$ is a multiple of $4$.
|
||||||
|
]
|
||||||
|
|
||||||
|
Here is the relevant code path for the optimization:
|
||||||
|
|
||||||
|
```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;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#note[ `onGround` is only ever updated via `move`, and this branch is the *only* place that
|
||||||
|
`ItemEntity.move` is called. ]
|
||||||
|
|
||||||
|
== Observable Drop Delay
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
This means if we know the remainder of $"id" slash 4$, we can predict which ages allow it to fall.
|
||||||
|
|
||||||
|
And the inverse: if we know which age allowed an item to fall, we can deduce the remainder of
|
||||||
|
$"id" / 4$.
|
||||||
|
|
||||||
|
#table(
|
||||||
|
columns: (auto, auto),
|
||||||
|
$"age"$, $"id"$,
|
||||||
|
[0], [0],
|
||||||
|
[1], [3],
|
||||||
|
[2], [2],
|
||||||
|
[3], [1],
|
||||||
|
)
|
||||||
|
|
||||||
|
This means if we know the remainder of $"age" / 4$, we can
|
||||||
|
|
||||||
|
|
||||||
|
#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.]
|
||||||
|
|
||||||
|
#example[
|
||||||
|
Consider this timeline. We spawn a few items, give them time to settle on the ground, then remove
|
||||||
|
their support. We observe when item $O$ begins to fall, and infer when we should expect the other
|
||||||
|
items to fall.
|
||||||
|
|
||||||
|
- `t+0`, spawn item $O$
|
||||||
|
- `t+1`, spawn item $A$
|
||||||
|
- `t+4`, spawn item $B$
|
||||||
|
- `t+4`, spawn item $C$
|
||||||
|
- `t+20`, remove support
|
||||||
|
- `t+22`, item $O$ begins to fall
|
||||||
|
|
||||||
|
Assuming no other entities spawn in this time period; these are the only things which increment
|
||||||
|
the counter.
|
||||||
|
|
||||||
|
At what ticks should we expect items $A$, $B$, and $C$ to begin to fall?
|
||||||
|
|
||||||
|
#solution[
|
||||||
|
Item $O$ falls when its age is $22 = 2 (mod 4)$, so its id must be $-2 (mod 4)$
|
||||||
|
|
||||||
|
Item $A$ spawns next, so its id must be $-1 (mod 4)$.
|
||||||
|
It can only fall when its age is $1 (mod 4)$.
|
||||||
|
It checks on ticks `t+2`, `t+6`, `t+10`, `t+14`, `t+18`, but is supported for all these.
|
||||||
|
On tick `t+22`, it will no longer be supported and will begin to fall.
|
||||||
|
|
||||||
|
Item $B$ spawns next, so its id must be $0 (mod 4)$.
|
||||||
|
It can only fall when its age is $0 (mod 4)$.
|
||||||
|
These are ticks `t+4`, `t+8`, `t+12`, `t+16`, `t+20`. So it falls immediately when the support
|
||||||
|
is removed.
|
||||||
|
|
||||||
|
Item $C$ spawns next, so its id must be $1 (mod 4)$.
|
||||||
|
It can only fall when its age is $-1 = 3 (mod 4)$.
|
||||||
|
These are ticks `t+7`, `t+11`, `t+15`, `t+19`, `t+23`.
|
||||||
|
|
||||||
|
- `t+20` remove support, item $B$ falls
|
||||||
|
- `t+22` items $O$ and $A$ fall
|
||||||
|
- `t+23` item $C$ falls
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
For simplicity, suppose we spawn all our items on the same tick. Then `this.tickCount` will be the
|
||||||
|
same for all of them, and we only need to consider the ID.
|
||||||
|
|
||||||
|
The result of this is that if you spawn several item entities, allow them to settle on a block, then
|
||||||
|
remove that block
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
= Transport Protocols
|
= Data Protocols
|
||||||
|
|
||||||
== Mod 4 Binary
|
== Mod 4 Binary
|
||||||
== Mod 2 Binary
|
== Mod 2 Binary
|
||||||
@@ -6,4 +6,3 @@
|
|||||||
== Transceivers
|
== Transceivers
|
||||||
== Logical Operations
|
== Logical Operations
|
||||||
== Transport Protocol Catalog
|
== Transport Protocol Catalog
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#import "/lib.typ": warn
|
#import "/lib.typ": callout, warn
|
||||||
|
|
||||||
#title()
|
#title()
|
||||||
|
|
||||||
@@ -14,4 +14,8 @@ Everything I know about Entity ID Wireless Redstone (EID Wireless) for Minecraft
|
|||||||
It does not work with any version of Bedrock Edition.
|
It does not work with any version of Bedrock Edition.
|
||||||
]
|
]
|
||||||
|
|
||||||
|
#callout(kind: "todo", label: "TODO LIST")[
|
||||||
|
#outline(title: none, target: figure.where(kind: "todo"))
|
||||||
|
]
|
||||||
|
|
||||||
#outline(title: [Outline], depth: 2)
|
#outline(title: [Outline], depth: 2)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#import "/lib.typ": note, tip, todo
|
#import "/lib.typ": note, tip, todo
|
||||||
|
|
||||||
= Interference
|
= Causes of Interference
|
||||||
|
|
||||||
We need precise control over the entity age and ID for this to work, so sources of interference are
|
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.
|
generally unexpected entity spawns that mess up the ID.
|
||||||
@@ -33,18 +33,19 @@ The solution is to either use a dedicated minecraft server, so that the client a
|
|||||||
separate processes, or install a mod which patches the game to use a separate counter for the
|
separate processes, or install a mod which patches the game to use a separate counter for the
|
||||||
client.
|
client.
|
||||||
|
|
||||||
#todo[Grab links for these mods and figure out exactly which versions they're good for.]
|
#todo[Singleplayer patch links][Grab links for these mods and figure out exactly which versions
|
||||||
|
they're good for.]
|
||||||
|
|
||||||
== Paper Servers <paper>
|
== Paper Servers <paper>
|
||||||
|
|
||||||
#todo[Figure out which versions of Paper broke the thing.]
|
#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
|
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
|
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)
|
few versions of Paper erroneously included this old optimization (on top of the mod 4 optimization)
|
||||||
which breaks EID Wireless.
|
which breaks EID Wireless.
|
||||||
|
|
||||||
#todo[Show the patch with the busted optimization.]
|
#todo[Broken Paper/Spigot code][Show the patch with the busted optimization.]
|
||||||
|
|
||||||
== Unloaded Chunks
|
== Unloaded Chunks
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ new entity IDs. Therefore any information encoded in the ID group offsets is des
|
|||||||
The best thing to do is to use a reload detector and lock the receiver output for one full cycle
|
The best thing to do is to use a reload detector and lock the receiver output for one full cycle
|
||||||
after a reload.
|
after a reload.
|
||||||
|
|
||||||
#todo[Get links to frost walker and sculk sensor reload detector designs.]
|
#todo[Reload detector designs][frost walker, sculk sensor, ???]
|
||||||
|
|
||||||
== Lazy Chunks
|
== Lazy Chunks
|
||||||
|
|
||||||
@@ -77,4 +78,4 @@ The best thing to do is to use a chunkloader to prevent the receiver ever being
|
|||||||
chunkloaders cannot be used, a lazy-chunk detector can be used to lock the outputs while the chunk
|
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.
|
is lazy-loaded and for one full cycle after it becomes entity-processing.
|
||||||
|
|
||||||
#todo[Get links to lazy-chunk detectors.]
|
#todo[Lazy chunk detector designs][falling entity, ???]
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
= Global Ticking Order <timing>
|
= Preventing Interference <timing>
|
||||||
|
|
||||||
== Tilesets
|
== Tilesets
|
||||||
|
|
||||||
=== Binary
|
=== Binary
|
||||||
|
|
||||||
=== Lexicographic
|
=== Lexicographic
|
||||||
|
|
||||||
=== Mixed Priority Jamming
|
=== Mixed Priority Jamming
|
||||||
|
|
||||||
== Block Event Delay
|
== Block Event Delay
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
|
#import "/lib.typ": todo
|
||||||
|
|
||||||
= Network Protocols
|
= Network Protocols
|
||||||
|
|
||||||
|
#todo["Network Protocol" is inaccurate]
|
||||||
|
|
||||||
== Collision Prevention
|
== Collision Prevention
|
||||||
=== Checkbit
|
=== Checkbit
|
||||||
=== Queue
|
=== Queue
|
||||||
32
lib.typ
32
lib.typ
@@ -33,7 +33,35 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}#let note = callout.with(kind: "note", label: "Note:")
|
#let note = callout.with(kind: "note", label: "Note:")
|
||||||
#let warn = callout.with(kind: "warn", label: "Warning:")
|
#let warn = callout.with(kind: "warn", label: "Warning:")
|
||||||
#let tip = callout.with(kind: "tip", label: "Tip:")
|
#let tip = callout.with(kind: "tip", label: "Tip:")
|
||||||
#let todo = callout.with(kind: "todo", label: "TODO:")
|
|
||||||
|
// #let todo = callout.with(kind: "todo", label: "TODO:")
|
||||||
|
#let todo(caption, ..args) = callout(kind: "todo", label: "TODO:", {
|
||||||
|
show figure: none
|
||||||
|
(
|
||||||
|
caption,
|
||||||
|
figure(kind: "todo", supplement: "TODO", caption: caption, none),
|
||||||
|
..args.pos(),
|
||||||
|
).join(parbreak())
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
#let example = callout.with(kind: "eg", label: "For example:")
|
||||||
|
|
||||||
|
#let details(body, label: "Details") = {
|
||||||
|
context if target() == "html" {
|
||||||
|
html.details({
|
||||||
|
html.summary(strong[Click for #label])
|
||||||
|
body
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
[
|
||||||
|
#strong(label):
|
||||||
|
#body
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let solution = details.with(label: "Solution")
|
||||||
|
|||||||
19
main.typ
19
main.typ
@@ -87,13 +87,14 @@
|
|||||||
it
|
it
|
||||||
}
|
}
|
||||||
|
|
||||||
rawdoc("index.html", include "content/00-index.typ")
|
rawdoc("index.html", include "content/index.typ")
|
||||||
doc("core.html", include "content/01-core.typ")
|
doc("core.html", include "content/core.typ")
|
||||||
doc("interference.html", include "content/02-interference.typ")
|
doc("data-protocols.html", include "content/data-protocols.typ")
|
||||||
doc("timing.html", include "content/03-timing.typ")
|
doc("interference-causes.html", include "content/interference-causes.typ")
|
||||||
doc("design.html", include "content/04-design.typ")
|
doc("interference-fixes.html", include "content/interference-fixes.typ")
|
||||||
doc("channels.html", include "content/05-channels.typ")
|
doc("design.html", include "content/design.typ")
|
||||||
doc("transport.html", include "content/06-transport.typ")
|
doc("channels.html", include "content/channels.typ")
|
||||||
doc("bulk.html", include "content/07-bulk.typ")
|
doc("bulk.html", include "content/bulk.typ")
|
||||||
doc("network.html", include "content/08-network.typ")
|
|
||||||
|
doc("network-protocols.html", include "content/network.typ")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[tools]
|
[tools]
|
||||||
tinymist = "latest"
|
tinymist = "0.15.0"
|
||||||
typst = "0.15.0"
|
typst = "0.15.0"
|
||||||
|
|
||||||
[tasks.watch-bundle]
|
[tasks.watch-bundle]
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6, nav {
|
h1, h2, h3, h4, h5, h6, nav, details > summary {
|
||||||
font-family: Mojangles, monospace;
|
font-family: Mojangles, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ nav > ol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
max-width: 88ch;
|
max-width: 68ch;
|
||||||
margin: 40px auto;
|
margin: 40px auto;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
@@ -105,3 +105,8 @@ header p + h1 {
|
|||||||
.callout h6 + p {
|
.callout h6 + p {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.callout *:last-child {
|
||||||
|
margin-block-end: 0;
|
||||||
|
padding-block-end: 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user