There are two kinds of events: independent and dependent.
For independent events, we don't send any tick; they are applied immediately.
For dependent events, we attach a so-called "init tick." This is the server tick during which any insertion, removal, spawn, or despawn occurred. If any of these actions happen, we set the "init tick" to the current server tick. If only a mutation happens on this tick, we don't increment the "init tick."
On the client side, we compare the last received "init tick" from replication with the "init tick" for the event, buffering it until the necessary replication arrives if it's earlier. Applying events when the server state differs archetype-wise or when entities are missing could cause issues.
Bug description
Replication is tied to the tick rate, and we update the tracked "init tick" when running the replication system. However, it's possible to spawn an entity and send an event where the "init tick" hasn't updated yet because replication occurs a few frames later due to the tick rate configuration.
For example, if the init tick is 10, and a user spawns an entity on the server and immediately sends an event, the client will receive the event with the correct "init tick" if replication happens on the same frame.
But if replication happens a few frames later, the client will receive the event with the previous init tick (10 in this case).
Possible solutions
Attach the next tick instead - not the "init tick," but the next regular server tick. The correct "init tick" will be calculated when replication occurs (i.e., when we iterate over the world). The advantage is that we don't have to wait for replication on server, but the client will still have to wait for the replication to arrive before applying the event, even if no insertion, removal, spawn, or despawn happened.
Wait for replication and send the correct "init tick" after the replication system runs. The downside is that we have to wait for the replication system to run (which doesn’t happen every frame), but the upside is that the client won't need to wait for the replication packet to arrive.
I didn't catch this issue in tests because replication runs every frame in them 😢
Context
There are two kinds of events: independent and dependent. For independent events, we don't send any tick; they are applied immediately.
For dependent events, we attach a so-called "init tick." This is the server tick during which any insertion, removal, spawn, or despawn occurred. If any of these actions happen, we set the "init tick" to the current server tick. If only a mutation happens on this tick, we don't increment the "init tick."
On the client side, we compare the last received "init tick" from replication with the "init tick" for the event, buffering it until the necessary replication arrives if it's earlier. Applying events when the server state differs archetype-wise or when entities are missing could cause issues.
Bug description
Replication is tied to the tick rate, and we update the tracked "init tick" when running the replication system. However, it's possible to spawn an entity and send an event where the "init tick" hasn't updated yet because replication occurs a few frames later due to the tick rate configuration.
For example, if the init tick is 10, and a user spawns an entity on the server and immediately sends an event, the client will receive the event with the correct "init tick" if replication happens on the same frame. But if replication happens a few frames later, the client will receive the event with the previous init tick (10 in this case).
Possible solutions
I didn't catch this issue in tests because replication runs every frame in them 😢