From 8fc002cda191df2c687f8dc0379398d4b0704395 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Sat, 20 Jun 2026 20:51:44 -0400 Subject: [PATCH] wip content --- content/01-core.typ | 70 --------- content/{07-bulk.typ => bulk.typ} | 0 content/{05-channels.typ => channels.typ} | 0 content/core.typ | 133 ++++++++++++++++++ .../{06-transport.typ => data-protocols.typ} | 3 +- content/{04-design.typ => design.typ} | 0 content/{00-index.typ => index.typ} | 6 +- ...terference.typ => interference-causes.typ} | 13 +- .../{03-timing.typ => interference-fixes.typ} | 5 +- content/{08-network.typ => network.typ} | 4 + lib.typ | 32 ++++- main.typ | 19 +-- mise.toml | 2 +- static/style.css | 9 +- 14 files changed, 202 insertions(+), 94 deletions(-) delete mode 100644 content/01-core.typ rename content/{07-bulk.typ => bulk.typ} (100%) rename content/{05-channels.typ => channels.typ} (100%) create mode 100644 content/core.typ rename content/{06-transport.typ => data-protocols.typ} (84%) rename content/{04-design.typ => design.typ} (100%) rename content/{00-index.typ => index.typ} (75%) rename content/{02-interference.typ => interference-causes.typ} (89%) rename content/{03-timing.typ => interference-fixes.typ} (75%) rename content/{08-network.typ => network.typ} (66%) diff --git a/content/01-core.typ b/content/01-core.typ deleted file mode 100644 index 7dc9913..0000000 --- a/content/01-core.typ +++ /dev/null @@ -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 diff --git a/content/07-bulk.typ b/content/bulk.typ similarity index 100% rename from content/07-bulk.typ rename to content/bulk.typ diff --git a/content/05-channels.typ b/content/channels.typ similarity index 100% rename from content/05-channels.typ rename to content/channels.typ diff --git a/content/core.typ b/content/core.typ new file mode 100644 index 0000000..87bc88c --- /dev/null +++ b/content/core.typ @@ -0,0 +1,133 @@ +#import "/lib.typ": callout, example, note, solution, 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 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 diff --git a/content/06-transport.typ b/content/data-protocols.typ similarity index 84% rename from content/06-transport.typ rename to content/data-protocols.typ index 0808b06..9e75789 100644 --- a/content/06-transport.typ +++ b/content/data-protocols.typ @@ -1,4 +1,4 @@ -= Transport Protocols += Data Protocols == Mod 4 Binary == Mod 2 Binary @@ -6,4 +6,3 @@ == Transceivers == Logical Operations == Transport Protocol Catalog - diff --git a/content/04-design.typ b/content/design.typ similarity index 100% rename from content/04-design.typ rename to content/design.typ diff --git a/content/00-index.typ b/content/index.typ similarity index 75% rename from content/00-index.typ rename to content/index.typ index 4e0e59c..0f7dec4 100644 --- a/content/00-index.typ +++ b/content/index.typ @@ -1,4 +1,4 @@ -#import "/lib.typ": warn +#import "/lib.typ": callout, warn #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. ] +#callout(kind: "todo", label: "TODO LIST")[ + #outline(title: none, target: figure.where(kind: "todo")) +] + #outline(title: [Outline], depth: 2) diff --git a/content/02-interference.typ b/content/interference-causes.typ similarity index 89% rename from content/02-interference.typ rename to content/interference-causes.typ index c4be27b..18418fe 100644 --- a/content/02-interference.typ +++ b/content/interference-causes.typ @@ -1,6 +1,6 @@ #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 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 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 -#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 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[Show the patch with the busted optimization.] +#todo[Broken Paper/Spigot code][Show the patch with the busted optimization.] == 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 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 @@ -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 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, ???] diff --git a/content/03-timing.typ b/content/interference-fixes.typ similarity index 75% rename from content/03-timing.typ rename to content/interference-fixes.typ index 12bab17..47e6286 100644 --- a/content/03-timing.typ +++ b/content/interference-fixes.typ @@ -1,8 +1,11 @@ -= Global Ticking Order += Preventing Interference == Tilesets + === Binary + === Lexicographic + === Mixed Priority Jamming == Block Event Delay diff --git a/content/08-network.typ b/content/network.typ similarity index 66% rename from content/08-network.typ rename to content/network.typ index 86dd297..d1d83b1 100644 --- a/content/08-network.typ +++ b/content/network.typ @@ -1,5 +1,9 @@ +#import "/lib.typ": todo + = Network Protocols +#todo["Network Protocol" is inaccurate] + == Collision Prevention === Checkbit === Queue diff --git a/lib.typ b/lib.typ index 8a231c7..683dc27 100644 --- a/lib.typ +++ b/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 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") diff --git a/main.typ b/main.typ index 5e4cf27..dba463a 100644 --- a/main.typ +++ b/main.typ @@ -87,13 +87,14 @@ it } - rawdoc("index.html", include "content/00-index.typ") - doc("core.html", include "content/01-core.typ") - doc("interference.html", include "content/02-interference.typ") - doc("timing.html", include "content/03-timing.typ") - doc("design.html", include "content/04-design.typ") - doc("channels.html", include "content/05-channels.typ") - doc("transport.html", include "content/06-transport.typ") - doc("bulk.html", include "content/07-bulk.typ") - doc("network.html", include "content/08-network.typ") + rawdoc("index.html", include "content/index.typ") + doc("core.html", include "content/core.typ") + doc("data-protocols.html", include "content/data-protocols.typ") + doc("interference-causes.html", include "content/interference-causes.typ") + doc("interference-fixes.html", include "content/interference-fixes.typ") + doc("design.html", include "content/design.typ") + doc("channels.html", include "content/channels.typ") + doc("bulk.html", include "content/bulk.typ") + + doc("network-protocols.html", include "content/network.typ") } diff --git a/mise.toml b/mise.toml index ab42b93..4724772 100644 --- a/mise.toml +++ b/mise.toml @@ -1,5 +1,5 @@ [tools] -tinymist = "latest" +tinymist = "0.15.0" typst = "0.15.0" [tasks.watch-bundle] diff --git a/static/style.css b/static/style.css index fc0668e..2527643 100644 --- a/static/style.css +++ b/static/style.css @@ -48,7 +48,7 @@ } } -h1, h2, h3, h4, h5, h6, nav { +h1, h2, h3, h4, h5, h6, nav, details > summary { font-family: Mojangles, monospace; } @@ -65,7 +65,7 @@ nav > ol { } body { - max-width: 88ch; + max-width: 68ch; margin: 40px auto; padding: 0 10px; } @@ -105,3 +105,8 @@ header p + h1 { .callout h6 + p { display: inline; } + +.callout *:last-child { + margin-block-end: 0; + padding-block-end: 0; +} \ No newline at end of file