Event cache options
Options
Hi,
I would like to use event cache in my turn-based game and I would like to cache all moves in a round of the game.
I still don't really get it after I read the documents.
1. What is the difference between ADD_TO_ROOM_CACHE & ADD_TO_ROOM_CACHE_GLOBAL?
2. What is the difference between REMOVE_CACHE & REMOVE_FROM_ROOM_CACHE? How to use it if I wanna raise an event and also clear all other events cached before.
3. What is the SLICE mechanism?
4. Are events included in the data of webhooks (State)?
BTW, any use cases to describe how to choose EVENTs or GAME PROPERTIES for turn-based game?
Thanks a lot!
Jo
I would like to use event cache in my turn-based game and I would like to cache all moves in a round of the game.
I still don't really get it after I read the documents.
1. What is the difference between ADD_TO_ROOM_CACHE & ADD_TO_ROOM_CACHE_GLOBAL?
2. What is the difference between REMOVE_CACHE & REMOVE_FROM_ROOM_CACHE? How to use it if I wanna raise an event and also clear all other events cached before.
3. What is the SLICE mechanism?
4. Are events included in the data of webhooks (State)?
BTW, any use cases to describe how to choose EVENTs or GAME PROPERTIES for turn-based game?
Thanks a lot!
Jo
1
Best Answer
-
Hi @JoH.
Sorry for the confusion. Maybe my phrasing in "Webhooks are unrelated to event caching." was a bit unclear. I rather mean "Webhooks are independent from event caching." - You can perfectly use eventCaching without webhooks, you can perfectly use webhooks without eventCaching and you can also use both together just as fine.
They are independent topics.
EventCaching is used to cache events for players that join the room after those events originally have happened, so that they still get them.
This has nothing to do with persisting a room to keep it in existence even after the last player has got offline, which is what webhooks are for.
If not a single player is actively playing in a room, then without webhooks that room get's just automatically closed and cleaned up, but with webhooks that whole room gets moved from RAM to storage, so that it can get reactivated later on if someone wants to continue playing in there.
If you set the game state to persistent, then the rooms properties and eventCache will also get stored and not get lost when preserving the room on storage for later reuse.
However you neither need any cached events for being able to use webhhoks to store a room, nor do you need to store a room, to be able to use eventCaching.
If player a) creates the room, player b) joins it and one of them sends around event 1 to all players and asks the server to cache it, then when later on player c) joins the room, that player receives that event, although he wasn't in the room yet, when it has originally been sent. Webhooks are not needed for this to happen.
Now, if all 3 players disconnect, then webhooks come into play for storing the room and when that happens, then the content of the eventCache and the game properties can also be stored as part of the game state, in case that it should still exists after reactivating that room.
Separately storing events or properties (through PathEvent and PathGameProperties) is only needed if you want to be able to access them without entering that room, for example to display them in an in game overview of different uncompleted games or if you want to display them n a website, etc.
a)
"If I use Events, I have to save each events via webhooks". No, as said before, you don't have to do that. Just set "IsPersistent" to true.
b)
No, don't let two different players write the same move to the game properties. Just let the player, who does the move write it to the properties at the moment when he does the move. Do not also send the move via events, just as property. That way either the move reaches the other players and is also stored in the properties or it just never happens, there is no in between in which the move could ever have happened for the other players without being stored in the properties. If the player disconnects just in the moment when it makes the move and therefor can't store the move in the properties anymore, then the other players have not seen that moves anyway. For them it just has never happened in the first place, so there is not only no need to preserve that move in that case, it even would explicitly corrupt the game state if you would still store it although it has not gone through, as in that case you would store a move that in fact has never happened in the first place.6
Answers
-
Hi JoH.
DO_NOT_CACHE // Default value (not sent). MERGE_CACHE // Obsolete - Merges this event's keys with those already cached. REPLACE_CACHE // Obsolete - Replaces the event cache for this eventCode with this event's content. REMOVE_CACHE // Obsolete - Removes this event (by eventCode) from the cache. ADD_TO_ROOM_CACHE // Adds this event to the room's cache ADD_TO_ROOM_CACHE_GLOBAL // Adds this event to the cache for actor 0 (becoming a "globally owned" event in the cache). REMOVE_FROM_ROOM_CACHE // Removes fitting events from the room's cache. REMOVE_FROM_ROOM_CACHE_FOR_ACTORS_LEFT // Removes events of players who already left the room (cleaning up). SLICE_INC_INDEX // Increments the slice index. SLICE_SET_INDEX // Sets the slice index to the specified value. SLICE_PURGE_INDEX // Deletes or purges the specified slice index. SLICE_PURGE_UP_TO_INDEX // Purges all cache slices up to the specified index (inclusive).
1.
See comments from above.
2.
REMOVE_CACHE is deprecated.
Please use REMOVE_FROM_ROOM_CACHE instead.
3.
Slices are a grouping mechanism for cached events.
Imagine you can send around a lot of cached events with slice 1 and then at some point something game specific happens which causes that none of the are needed to be cached anymore. You now can just purge them from the cache all at once.
For example when a game uses some type of levels, then a player that joins the room, while the players inside have already pushed the game to level 25, then the cached events from room 1-24 may not be relevant at all anymore for the new player, that joins the game in level 25, so there is no need to let the serve send all those outdated events to him.
This can be critical for long running games that cache lots of events.
Depending on the amount and average size of cached events the initial load of data that gets sent to late joiners may become that huge, that those new clients connections get struck dead by the pure massive amount of data that comes in at once.
Being able to group event in slices give you a tool to prevent this scenario by limiting the amount of data.
4.
Webhooks are unrelated to event caching.
Client::opRaiseEvent() has an optional bool parameter 'forwardToWebhook'. Its default value is 'false'. If you pass 'true', then the event that gets raised by that call to the function gets forwarded to webhooks (provided, you have set up webhooks for that appId in your dashboard, of course).1 -
I would like to confirm the order of events which are sent with ADD_TO_ROOM_CACHE or ADD_TO_ROOM_CACHE_GLOBAL.
In Ordered delivery section in the link "https://doc.photonengine.com/en/realtime/current/reference/cached-events" , it says the order depends on event code and merge if it's a hashtable.
Is that the deprecated part?
I would like to make sure the order is exactly the same as Photon Cloud received in my game.
Ex:
1. Event code = 0, ADD_TO_ROOM_CACHE
2. Event code = 1, ADD_TO_ROOM_CACHE
3. Event code = 1, ADD_TO_ROOM_CACHE
4. Event code = 1, ADD_TO_ROOM_CACHE_GLOBAL
5. Event code = 2, ADD_TO_ROOM_CACHE
6. Event code = 1, ADD_TO_ROOM_CACHE
Can I assume that the order never change?
Thanks for your help!
Jo0 -
Hi JoH.
Photon has two EventCache implementations.
The original and now deprecated variant used the event codes MERGE_CACHE, REPLACE_CACHE and REMOVE_CACHE and determined the order based on eventCodes like described in the doc that you have linked.
The new variant for which we have added the other codes uses the same order like when the events get originally sent.
So, yes, with those new codes you can assume that the order never changes, but only when you either choose TCP as protocol (which implicitly makes everything reliable) or mark all events as reliable.
When events are marked unreliable and UDP gets used, then for both, the raiseEvent operations sent to the server as well as the events sent from the server to the other clients, some might get lost completely and some might just outrun others and arrive earlier. This is true independently from the question if the event is coming from a cache or not.
Some more background info on you original question 1:
When a player leaves a room (not, when he just becomes inactive, but when he leaves the room entirely), then on events that are cached for his player number get deleted from the cache.
If this is desired, then ADD_TO_ROOM_CACHE is the appropriate choice. If this is not desired, then ADD_TO_ROOM_CACHE_GLOBAL is more appropriate, as that means, the event only gets removed from the cache, when either a client explicitly requests this or when the whole room gets deleted.
Unfortunately the new EventCache implementation isn't covered very well in the documentation, yet.
We will work on improving this in the future.0 -
Hi
thanks for your detail explanation.
Let describe my needs and my understanding to confirm it again.
1. Event code = 0, ADD_TO_ROOM_CACHE , player 1
2. Event code = 1, ADD_TO_ROOM_CACHE , player 1
3. Event code = 1, ADD_TO_ROOM_CACHE , player 2
4. Event code = 1, ADD_TO_ROOM_CACHE_GLOBAL, , player 3
5. Event code = 2, ADD_TO_ROOM_CACHE , player 1
6. Event code = 1, ADD_TO_ROOM_CACHE , player 2
a) If player 2 and player 3 left rooms, player 4 join the room, he will get the event 1,2,4,5 ?
my game is kind of card game, so even player 2 left completely , his moves are still important to the game.
So I should use ADD_TO_ROOM_CACHE_GLOBAL for all moves, right?
b) Leaving the room entirely means calling Client::opLeaveRoom(false) ?
c) REMOVE_FROM_ROOM_CACHE clears both ADD_TO_ROOM_CACHE_GLOBAL & ADD_TO_ROOM_CACHE ?
d) I would like to save the all moves via webhooks, but there is no cache event. What I wanted is to save all moves once when a hand ended. But since there is no cache event, I need to use the event webhook to accumulate all moves so there are more requests. Should I use game properties instead of Cache? Any suggestion is helpful.
Thanks a lot!
Jo0 -
Hi JoH.
a)
If player 4 joins after player 2 and 3 have left, then yes, player 4 will get events 1, 2, 4 and 5.
b)
Either that or if he leaves with Client::opLeaveRoom(true) or disconnects and additionally does not come back within the time specified as playerTtl (ttl stands for time to live) on room creation, which determines, how long players stay inactive, until Photon considers them left.
c)
It removes whatever event you specify to remove in the other parameters of opRaiseEvent (eventCode, targetActors and filters specified as a Hashtable in parameters).
d)
Sorry, but I don't get what you mean. Would you like to eleborate please, what exactly you are doing, what you expect to happen and what actually happens? Thanks.
Aside from that: Yes, game properties might be a simpler way to achieve this. You might want to take a look at demo_memory, which uses game properties to implement a turn based memory game, which state is stored even when none of the players is online.0 -
d) When maybe 10 moves occurred, a round finished. Then I would like to record it to other server by web hooks and clear the Cache in the room.
1. If I use "events" to send moves, my webhooks endpoint need to record all moves by GaemEvent web hook which is 10 TIMES in this case. There is no backup if any network trouble or computing resource issue accidentally happened. And I cannot recover game when recreate the game because I save nothing in Game properties.
2. if I use "game properties" to send moves (also as cache for rejoin) , my webhooks could save only ONCE for the round by raising a end round event or detect some flag in game properties. But there might be some race condition for writing to the game properties. (If someone no response, a master client may send moves for the idle user)
I would like to use 1. for the exact order of every move, but the concern is that there is no event cached in the POST content.
Any suggestions for this? Or should I mix up it?
Thanks a lot!0 -
Hey @JoH !
Although what you are asking is not very clear I will try to make a suggestion:
From what I understood, you want to send multiple events during a round of your game. Then once the round ends you want those events to be saved.
You should raise events with the ADD_TO_ROOM_CACHE_GLOBAL flag.
Now the question is how do you want to load or access those events:
- ONLY when rejoining the game:
If you want to receive the events in order every time you rejoin the game, you should only save the Room state inGameClose Type=='Save'
webhook endpoint. Make sureIsPersistent
is set to true for your application.
Optionally, after each round send another event to increase room cache slice so you will have a slice per round. Then later, you can delete each event from cache separately or even delete whole round events at the same time. You can also remove events until a specific round.
- Anytime:
If you want to access the events data anytime and from outside Photon then you should save each event independently in its respective GameEvent webhook call.
HL0 -
Hi @JoH.
1.
If you send the events marked as reliable or if you use TCP then it is guaranteed by Photon that either all events arrive at the server or that a disconnect will occur for a client which connection is that bad that it just can't get the message through, but in that disconnect case the event will also never reach the other players, so that players move basically never happens and therefor it should not be stored in the webhooks.
2.
Same as opRaiseEvent() all game property setting functions have a parameter called 'forwardToWebhook'. When you set that to true, the server will send that game property change to the webhooks, so there is no need for a special end of round event.
Also you race condition issue should be a non-issue as the same is true for game properties as for events: If the clients already ask for forwarding the change to the webhooks when the change occurs, then if the change does not reach the server, then the server not only can't send it to the webhooks, but also not to the other clients, so that that change basically never happened in the first place and therefor should not be stored in the webhooks anyway.
Also, what @JohnTube has mentioned, is true:
You do not need to store your events or game properties in the webhooks at all, if you just want to reload the game later on: Just set the game state to persistent in the webhooks and cached events and game properties will survive and exist as long as the room itself gets stored in the webhooks.
However, the option to also store cached events and game properties additionally separately from the game state exists not only to be able to access them from external services, but also so that you can use Client::opWebRpc() and Listener::WebRpcReturn() to retrieve this info from the Client even when the client is not inside that room.
That way if a player has for example 20 unfinished games going on, you can present him up to date info about all those games (for example: "it's your turn now in room 15", or "player d in room 12 has made his move") without the need for the Client to always first enter and leave all those rooms all the time.1 -
Hi, @JohnTube , @Kaiserludi
Really thanks for your help.
I think I didn't explain well in the previous comments.
1. According to Kaiserludi's first answer "Webhooks are unrelated to event caching." , even "GameClose Type=='Save'" webhooks occurs, there is no event related data to save, isn't there?
But Kaiserludi said "Just set the game state to persistent in the webhooks and cached events and game properties will survive and exist as long as the room itself gets stored in the webhooks." in the last comment. I got a little bit confused.
Do cached events still exist after setting the game state to persistent in the webhooks?
2. What I asked is about saving all moves via webhooks so I can access "from outside Photon". So I reconsidered if I should use a)Events OR b)Game properties.
a) If I use Events, I have to save each events via webhooks. If any webhooks of event (move) failed, the move may be lost forever. Ex: Assuming there are 10 moves in a round, the webhook endpoint need to save moves 10 TIMES. If one of the 10 webhook requests failed (due to network issue or endpoint computing resource issue or whatever), the history of the round is not completed.
Any suggestion to handle this kind of error?
b) if I use GameProperties ({"move1":"someMove","move2":"someMove2"}
..etc. All moves are saved in Properties, so it also can be treated as cache) , when the round ended, the webhook endpoint can save all moves ONLY ONCE.
But there might be a race condition (which is not related to webhooks). That is: The Master Client may raise an event (folding cards) for the player who is disconnected, so the Master client and the player (maybe re-join anytime) may write to the same key of game properties, so the later move overrides the previous one and that is unwanted. In this case, using event cache can prevent it because moves are in order.
Sorry for unclear questions before, I tried my best to explain my thought here.
Thanks a lot.0 -
@JoH
First, please take some time to read about webhooks and their FAQs.
Now let's try answering your questions:
1. "Events caching", "webhook forwarding" and "IsPersistent" are three different things.
- Events caching: events sent with caching flag enabled will have its data added to room state cache. Cached events' main purpose is to send or resend those events to joining actors.
- "IsPersistent": webhook setting to enable triggering a webhook when the room is about to be removed from Photon servers in order to save its state. That state will include the events cache among other things.
- Webhook forwarding: option available withOpSetProperties
andOpRaiseEvent
to trigger a respective webhook. Webhooks' main purpose is to be able to save games' data.
Yes. If you take care of saving the state in your GameClose's web service endpoint then all cached events will be saved.
2. a) Please stop worrying about network failure. As @Kaiserludi already explained to you: if aGameEvent
webhook fails then theOpRaiseEvent
should fail.
If you are still worried about events not being transmitted or saved, you can add an "ACK event" per "event". Optionally you can remove the respective event from cache by setting the "ACK event" caching flag to REMOVE_FROM_ROOM_CACHE. If you need more details about this approach let me know as I'm using it in my game.
2. b) For your case you should not use custom properties in my opinion. I don't know if you are aware of this but: "the webhook endpoint can save all moves ONLY ONCE." means that you are callingOpSetProperties
withwebForward==true
exactly ONLY ONCE. I don't consider the issue you are raising as "race condition" but rather a design problem in your game.0 -
Hi @JoH.
Sorry for the confusion. Maybe my phrasing in "Webhooks are unrelated to event caching." was a bit unclear. I rather mean "Webhooks are independent from event caching." - You can perfectly use eventCaching without webhooks, you can perfectly use webhooks without eventCaching and you can also use both together just as fine.
They are independent topics.
EventCaching is used to cache events for players that join the room after those events originally have happened, so that they still get them.
This has nothing to do with persisting a room to keep it in existence even after the last player has got offline, which is what webhooks are for.
If not a single player is actively playing in a room, then without webhooks that room get's just automatically closed and cleaned up, but with webhooks that whole room gets moved from RAM to storage, so that it can get reactivated later on if someone wants to continue playing in there.
If you set the game state to persistent, then the rooms properties and eventCache will also get stored and not get lost when preserving the room on storage for later reuse.
However you neither need any cached events for being able to use webhhoks to store a room, nor do you need to store a room, to be able to use eventCaching.
If player a) creates the room, player b) joins it and one of them sends around event 1 to all players and asks the server to cache it, then when later on player c) joins the room, that player receives that event, although he wasn't in the room yet, when it has originally been sent. Webhooks are not needed for this to happen.
Now, if all 3 players disconnect, then webhooks come into play for storing the room and when that happens, then the content of the eventCache and the game properties can also be stored as part of the game state, in case that it should still exists after reactivating that room.
Separately storing events or properties (through PathEvent and PathGameProperties) is only needed if you want to be able to access them without entering that room, for example to display them in an in game overview of different uncompleted games or if you want to display them n a website, etc.
a)
"If I use Events, I have to save each events via webhooks". No, as said before, you don't have to do that. Just set "IsPersistent" to true.
b)
No, don't let two different players write the same move to the game properties. Just let the player, who does the move write it to the properties at the moment when he does the move. Do not also send the move via events, just as property. That way either the move reaches the other players and is also stored in the properties or it just never happens, there is no in between in which the move could ever have happened for the other players without being stored in the properties. If the player disconnects just in the moment when it makes the move and therefor can't store the move in the properties anymore, then the other players have not seen that moves anyway. For them it just has never happened in the first place, so there is not only no need to preserve that move in that case, it even would explicitly corrupt the game state if you would still store it although it has not gone through, as in that case you would store a move that in fact has never happened in the first place.6