Event Caching
The Event Cache
An event sent with a caching option is stored by the server as part of the room state. Every player who joins later receives the cached events automatically, as if it had been present when they were sent. The operation is selected per send through the caching option of the event options, see Custom Events for the sending basics.
A joining player first receives the room and player properties, then the cached events, and only then the live events sent after the join. The cached events arrive in the order they reached the server, so the replay reproduces the original sequence. Only events sent unreliably can deviate from the send order, because their arrival order at the server is not guaranteed.
The smallest useful example of the pattern is a round timer: cache one round-start event carrying the start timestamp. Every player who joins mid-round receives it during the replay and derives the remaining round time locally, with no extra protocol.
Caching Options
The caching option selects the cache operation and defaults to not caching: EventOptions.Caching (an EventCache) in C++, the caching parameter of realtime_send_event() in C and the caching option of sendEvent() in JavaScript.
| C++ | C | JavaScript | Meaning |
|---|---|---|---|
DoNotCache |
0 |
'doNotCache' |
Deliver to the current players only. The default. |
AddToRoomCache |
4 |
'addToRoomCache' |
Add the event to the room cache, tied to the sending player. |
AddToRoomCacheGlobal |
5 |
'addToRoomCacheGlobal' |
Add the event to the room cache without an owner, so it outlives the sender. |
RemoveFromRoomCache |
6 |
'removeFromRoomCache' |
Remove matching events from the room cache. |
RemoveFromRoomCacheForActorsLeft |
7 |
'removeFromRoomCacheForActorsLeft' |
Remove cached events of players who already left. |
Every value other than not caching manages the room cache, an ordered list that can hold any number of events.
An event is not added to any cache when its options target a subset of the room: the master-client target group, a non-empty list of target players or a non-zero interest group all disable caching for that send. The Event Options table lists the option names per language.
Actor Cache and Room Cache
Every cached event is stored with an owner, and that owner determines the event's delivery and lifetime.
Events added to the room cache tied to the sending player are owned by that player, the actor, so their lifetime is tied to that player.
Events added without an owner belong to the global room cache and are not tied to any player: their owner is the room itself, addressed as player number 0 when removing.
Cached events are replayed to every joining player, and rejoining is no exception: a player that leaves and comes back receives its own cached events again.
For global room cache events the sender's player number is always 0.
When a player leaves the room for good, the server drops that player's cached events automatically (see the cleanup notes below). A global room cache event survives until it is removed explicitly or the room closes. Choose the player-owned cache for state that should disappear with its player, and the global room cache for world state that must outlive whoever happened to send it.
Cache a level-state event so late joiners receive it, and remove it once it no longer applies:
C++
struct LevelState
{
int32_t OpenedDoors;
};
constexpr uint8_t LevelStateCode = 4;
LevelState state{3};
// Cached without an owner: it survives even if this client leaves.
EventOptions cache;
cache.Caching = EventCache::AddToRoomCacheGlobal;
client.SendEvent(LevelStateCode, state, cache);
// Later: remove the matching cached event once the state is obsolete.
EventOptions remove;
remove.Caching = EventCache::RemoveFromRoomCache;
client.SendEvent(LevelStateCode, state, remove);
C
typedef struct { int32_t openedDoors; } LevelState;
#define LEVEL_STATE_CODE 4
#define CACHE_ADD_TO_ROOM_CACHE_GLOBAL 5
#define CACHE_REMOVE_FROM_ROOM_CACHE 6
LevelState state = {3};
/* Cached without an owner: it survives even if this client leaves. */
realtime_send_event(client, LEVEL_STATE_CODE,
(const uint8_t*)&state, (int32_t)sizeof(state),
1, 0, 0, NULL, 0, 0,
CACHE_ADD_TO_ROOM_CACHE_GLOBAL, /* caching */
0);
/* Later: remove the matching cached events once the state is obsolete. */
realtime_send_event(client, LEVEL_STATE_CODE,
NULL, 0, /* the payload plays no part in the removal filter */
1, 0, 0, NULL, 0, 0,
CACHE_REMOVE_FROM_ROOM_CACHE,
0);
/* caching is the numeric EventCache, and only 0 and 4 to 7 are valid,
so any other value falls back to DoNotCache. The removal send passes
a NULL payload because the filter is built from the event code and
the target players alone. */
JavaScript
const LEVEL_STATE_CODE = 4;
const state = { openedDoors: 3 };
// Cached without an owner: it survives even if this client leaves.
client.sendEvent(LEVEL_STATE_CODE, state, { caching: 'addToRoomCacheGlobal' });
// Later: remove the matching cached event once the state is obsolete.
// The payload plays no part in the removal filter.
client.sendEvent(LEVEL_STATE_CODE, null, { caching: 'removeFromRoomCache' });
Removing Cached Events
A send that removes events from the room cache does not deliver anything. Its options form a filter, and every cached event matching the filter is removed from the room cache. Two fields make up the filter: the event code and the selected senders, the payload plays no part in the match.
An event code of 0 acts as a wildcard and matches every cached code.
The target players select which players' cached events are searched: include 0 to address the global room cache entries, or leave the list empty to search the entries of all senders.
The payload is ignored entirely: a removal with a different payload, or with none at all, removes the same entries, so same-code events cannot be removed selectively by content.
When a player leaves for good, the server removes that player's cached events automatically, so joins do not replay the past of players who are gone.
A player that is merely inactive, it left with willComeBack in a room with a player TTL, keeps its cached events until it times out or leaves for good.
To clean up the global room cache entries of players that already left, send with the caching option RemoveFromRoomCacheForActorsLeft in C++, 7 in C or 'removeFromRoomCacheForActorsLeft' in JavaScript.
Keeping the Cache Small
The cache is replayed on every join, so its size directly stretches every future join. A joining player is fully in the room only once the replay completes, and live events sent in the meantime are delivered only after the cached ones. Keep the cache to what a late joiner genuinely needs.
The server limits the total number of cached events per room, counting player-owned and global room cache entries together. A room that exceeds the limit is closed: the players already in the room can continue, but no new player can join and inactive players cannot rejoin.
Remove events with RemoveFromRoomCache the moment they stop being relevant.
For state with a single current value per room, custom properties are often the better fit than a growing event cache.
Last updated on
Back to top