wip content
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user