A gametick (commonly abbreviated “gt”, or simply “tick”) is a part of the game loop where the game logic is processed.
Many different things are done in a tick, they are grouped into what we call tick phases.
These are the ones commonly used for technical Minecraft:
Here is a detailed breakdown of the server tick:
runServer()
net.minecraft.server.MinecraftServer.runServer()
This is the main server loop.
The main loop:
net.minecraft.server.MinecraftServer.tick()
If an exception occurs this loop will shut down the server and try to create and save a crash report.
MinecraftServer.tick()
net.minecraft.server.MinecraftServer.tick()
This method is called in runServer()
net.minecraft.server.MinecraftServer.tickWorlds()
MinecraftServer.tickWorlds()
net.minecraft.server.MinecraftServer.tickWorlds
This method is called in MinecraftServer.tick()
net.minecraft.server.ServerWorld.tick()
, which is the main in-game loopServerWorld.tick()
net.minecraft.server.ServerWorld.tick
This method is called in MinecraftServer.tickWorlds()
The following actions/processes are performed during every tick:
synchedBlockEvent
hashmap and execute the block events stored in itnet.minecraft.world.border.WorldBorder.tick
Called in the game loop.
The only thing that will happen during this “ticking” of the world border is checking if the world border needs to change in size, and executing that.
See separate article: Weather
See separate article: Block Ticks
TODO
(just before block events since 1.17.1, was before tileticks)
Block events are processed in this phase. This includes things such as:
Block events are stored in a LinkedHashSet. The game loops through block events and processes them. Piston block events can activate and schedule another block event that is added a the end of the LinkedHashSet.This allow chaining multiple blockevents in the same tick.
Blockevents are sent directly to the client the execute them also clientside.
In the block event phase, the game calls processSyncedBlockEvents()
from net.minecraft.server.ServerWorld
. All block events are stored in the synchedBlockEventQueue
. This queue is an ObjectLinkedOpenHashSet
. This is a type specific linked hash set which uses a hash table to represent a set. For more information about hashsets see Earthcomputer’s Video. This is a linked hash set which means a list is stored as well as a hash set. This list is used for iteration.
public int hashCode() {
int i = this.pos.hashCode();
i = 31 * i + this.block.hashCode();
i = 31 * i + this.type;
i = 31 * i + this.data;
return i;
}
this.pos.hashCode()
:
public int hashCode() {
return (this.getY() + this.getZ() * 31) * 31 + this.getX();
}
this.block.hashCode()
:
public int hashCode() {
int i = this.self.hashCode();
i = 31 * i + this.other.hashCode();
i = 31 * i + this.facing.hashCode();
return i;
}
this.self.hashCode()
, this.other.hashCode()
and this.facing.hashCode()
:
Uses the standard hashCode()
method from the Object
class in java. self
and other
are net.minecraft.block.BlockState
instances. facing
is a net.minecraft.util.math.Direction
instance.
This means that a block event’s hashcode depends on:
processSynchedBlockEvents
will go through the syncedBlockEventQueue
until it’s empty. First there will be a check. Here the game will get the BlockState
of the block in the position where the BlockEvent
is called. If this BlockState
corresponds to the block state of the BlockEvent
then processing will continue. If not the code will go to the next object in the queue. This can happen when for example the block is broken or has changed state already.
After this the onSynchedBlockEvents
method will be called. After this there are 2 possibilities. It’s either a block entity or a normal block. There is a difference between those two in what happens. More about this will be discussed later
addSynchedBlockEvent()
):The order in which block events happen is both locational and directional since it is stored in a hashset.
Things that call net.minecraft.server.world.serverworld.addSyncedBlockEvent
:
Block Name | Type | Data | Called When |
---|---|---|---|
Bell Block | 1 | direction.getId() (side bell was hit from) |
hit |
Noteblock | 0 | 0 | played |
Extending Piston | 0 | direction piston is facing | |
Retracting Piston | 1 | direction piston is facing | |
Moving Piston | 2 | direction piston is facing | |
Chest | 1 | amount of players viewing inventory | opening or closing |
Shulker Box | 1 | amount of players viewing inventory | opening or closing |
Ender Chest | 1 | amount of players viewing inventory | opening or closing & every 20gt |
End Gateway Block | 1 | 0 | after teleporting entity |
Mob Spawner | 1 | 0 | updating spawns |
onSynchedBlockEvent()
):So now we know when a block event gets scheduled, how its hash works, what actions schedule block events etc. But we don’t yet know what it does, why block events are used etc.
First the onSynchedBlockEvents()
method from AbstractBlock
gets called. This will then call a different method depending on if the block is a normal block or a block entity.
Block entities is the biggest category. Here the onSynchedBlockEvents()
method from BlockWithEntity
gets called. First will super()
the method from AbstractBlock
. This is a deprecated method and will always return false, thus won’t do anything. After this it will check if the block entity isn’t null. Now blockEntity.onSyncedBlockEvent(type, data);
gets called. This will first call the block specific method and then super()
some methods.
The bell block will ignore the block event if the type isn’t 1 (return false
). If the type is 1 the following happens:
true
gets returned.If the type is 1, it will set viewerCount = data
and return true
. Otherwise it will return false
.
If the type is 1, it will set the teleportCooldown = 40
and return true
. Else it will return false
.
If the type is 1, it will set viewerCount = data
and return true
. Else it will return false
.
If the type is 1 and the call is client-side, spawn delay = min spawn delay
and true
is returned. Else false
is returned.
if type isn’t 1, return false
.
If data = 0 close the shulker box and return true
if data = 1 open the shulker box and return true
For normal blocks it’s a little simpler. Here the block specific method gets called immediately. After this super()
methods get called.
All the code about block events in comparator block classes doesn’t do anything. It is most likely leftover or something. There’s also no possible way to have a block event on the position of a comparator execute…
Play the note, display the particles and make the sound in game, then return true
.
TODO
// this needs to be separated into another article
Hierarchy:
net.minecraft.server.world.ServerWorld.tickEntity()
tick()
method
net.minecraft.entity.mob.MobEntity.tick()
is called via super()
net.minecraft.entity.LivingEntity.tick()
is called via super()
net.minecraft.entity.Entity.tick()
is called via super()
net.minecraft.entity.mob.MobEntity.basetick()
gets callednet.minecraft.entity.LivingEntity.basetick()
is called via super()
net.minecraft.entity.Entity.basetick()
is called via super()
…
More detail on this will come in the next sections:
ServerWorld.tickEntity()
First the game looks if the entity needs to be ticked. It doesn’t tick the entity if either the entity is an object of the player class or if getChunkManager().shouldTickEntity(entity)
returns false
.
If either of these conditions aren’t satisfied then this.checkEntityChunkPos(entity)
is called.
If the entity needs to be ticked the following happens:
position
, pitch
, yaw
, etc. variables get reassigned from entity.prevYaw
to entity.yaw
age
gets advanced by 1In the following sections we’ll be looking at everything that happens in a chronological order
SpecificMob.tickEntity()
Most mobs override the tick()
method from MobEntity.java
. In this method the method from MobEntity
gets supered.
We’ll look at specific mobs after we’ve described the general way in which mobs work.
MobEntity.tick()
This overrides the tick()
method from LivingEntity
.
tick()
method from LivingEntity
gets superedLivingEntity.tick()
This overrides the tick()
method from Entity
.
A lot of this stuff doesn’t apply to mobs, only to players
tick()
method from Entity
gets superedtickActiveItemStack()
is called (this basically updates the item someone is holding)tickMovement()
(see later)Entity.tick()
baseTick()
method gets called.MobEntity.baseTick()
This overrides the baseTick method from LivingEntity.
super()
method gets calledLivingEntity.baseTick()
This overrides the baseTick method from Entity
super()
method gets calledEntity.baseTick()
This is the upper most baseTick()
method
tickMovement()
This is called within LivingEntity.tick()
If it’s a passive animal the first method that will be called is the method from net.minecraft.entity.passive.PassiveEntity
If it’s not there’s a mob specific method. In the next sections i will go over all the tickMovement()
methods from PassiveEntity.tickMovement()
all the way up to LivingEntity.tickMovement()
. For non passive entities the specific methods will be adressed in the sections about that mob.
PassiveEntity.tickMovement()
This overrides the tick()
method from MobEntity
.
super()
methodMobEntity.tickMovement()
This overrides the tick()
method from LivingEntity
.
LivingEntity
LivingEntity.tickMovement()
Most of the movement stuff happens in this method.
jumpingCooldown
tickNewAI()
(see next section)tickNewAI()
riptideTicks
and tick riptideMobEntity.tickNewAI()
this is being called in LivingEntity.tickMovement()
and handles all of the AI stuff. This is separated into sections based on the profiler loggings.
targetSelector
Here targetSelector
gets ticked, which is a GoalSelector
object.
What happens in the GoalSelector.tick()
method:
goalSelector
Call this.goalSelector
which is an GoalSelector
object.
What happens in the GoalSelector.tick()
method:
Call this.navigation
which is an EntityNavigation
object.
Main article: Mob Tick
This method is different for each mob and will be addressed for each mob specifically.
Calls MoveControl.tick()
.
looking
jumping
Tile/Block entities are processed in this phase. This includes things such as:
Player inputs are processed in this phase. This includes things such as:
There are other things that are calculated in a ticks that isnt specific to a phase, for example rails and redstone dust is calculated recursivly independent from the ticks and can happen in all of them. These components are called “recursive updators” or “instant updators”.