johntube
started a topic on Wed, 16 September 2015 at 5:28 PM
Hi PlayFab community,
I want to seek your advice for a best approach in a design of an invitation system in CloudScript.
Requirements and constraints:
It's for 2 players (pvp) games. Invitations can also be called challenges.
The invitation system in question requires "2 ways consent": the challenged/receiver should accept.
Although I like the optimistic approach of challenging opponents and entering the game directly to start playing, I decided not to do it in this game as each player can start with some power ups equipped. If the opponent declines the challenge, what should happen to those power ups? "redeemed" or lost for ever?
Also pending invitations cloud be canceled by the sender.
Pending invitations expire after a while.
First let's start with the things that I'm certain of their necessity:
Each user should have a shared group with id = {playfabId}_InvitationsList. Or any other format / suffix / prefix.
Invitations are unique and should have unique IDs. I'm using the same generator for the game IDs as the invitation will become a game later and keep the same ID. Of course, even if exchanged, this is invisible to the user.
CloudScript file has at least 2 handlers to be able to SendInvitation and UpdateInvitation (the latter can be exploded to AcceptInvitation, DeclineInvitation, CancelInvitation): 2 or more will make your ClourScript file long and ugly enough. It depends on how ugly you want it. Arguments parsing ugly with extra bandwidth consumption for the user or longer code with a lot of repetitive stuff. "DRY...and KISS".
Having all the above in place after hours of pain with CloudScript debugging and JSON to C# parsing I managed to have the following:
- Player1 sends invitation calls RunCloudScript, ActionId="SendInvitation" with Args
{
"GameId", // InvitationId
"PlayerName", // sender or challenger, to be saved and used in the Push Notification msg!
"OpponentId", // receiver or challenged
"OpponentName", // receiver or challenged, to be saved for future Push Notification use.
}
In the respective handler, invitation data should be saved as a value in the 2 shared groups acting as invitation lists of both players. The key is the invitationId. InvitationStatus enum is added to the invitation data, it represents values like SentPending, ReceivedDeclined, SentExpired, ReceivedCanceled, etc.
5 server API calls in SendInvitation only:
2*GetSharedGroupData: to authorize action (check uniqueness, etc)
2*UpdateSharedGroupData: to add new invitation
1*SendPushNotification
Player2 receives invitation via Push or by Polling (Periodic or Pull to refresh). Other ways exist like Photon Chat, Facebook API, etc.
Player2 Accepts or Declines by calling the appropriate handler.
5 API calls also:
2*GetSharedGroupData: to authorize operation (check invitation exists and state transition is possible, etc.) + CHECK EXPIRATION
2*UpdateSharedGroupData: to update new state
1*SendPushNotification: to inform opponent
can be reduced to 4 when not checking existence and status of invitation in shared group of the original sender (!=currentPlayFabId).
The same logic can also apply to CancelInvitation.
Now instead of being happy of what I achieved I started questioning everything (believe me this happened to me infinite times especially with CloudScript, the estimated time of completion of my game has doubled due to major breaking refactoring):
Do I need a Master_InvitationsList? If I don't need it right now, maybe I will need it in the future who know? ==> I will save 2 API call to server.UpdateSharedGroup per UpdateInvitation handler(s). Same shared invitation data for both. In this case what to save as value in the players' invitations' list? keep it empty? With Polling in mind, a GetInvitationsList handler (data included) will require 1 extra API call to server.GetSharedGroupData. Please let me explain:
first get list of invitations of currentPlayFabId
then get their respective data using the previously obtained invitationIDs (shared group keys)
CHECK EXPIRATION
compare with what the list sent by the user:
if an id is missing return the whole data to the user
if id exists but state is different return state only
Instead of marking invitations with outgoing or incoming maybe I need to keep 2 lists (2 shared groups) per user: IncomingInvitations and OutgoingInvitations? Will double each previous call to get list of invitations though.
Argh...too many questions, I'm not against trying everything and I love JavaScript but...I need to say this outloud: CloudScript I hate you.
Is this "premature optimization"...the root of all evil?