TODD KERPELMAN: Hello, and welcome back
to the second half of our in-depth guide
to Saved Games using Google Play game services.
Yes, that's right.
I said second half.
This is the second part of a two-part series.
If you haven't watched part one yet, you really should.
Otherwise, this video isn't going to make any sense.
Go on.
It's OK.
I'll wait.
[HUMMING] OK.
Ready?
Let's go.
So here's where we move into black diamond difficulty.
So put on your safety goggles, if you have them.
It's going to get kind of rough, but I think you can handle it.
You seem pretty sharp.
Dealing with saved game conflicts
is a situation you're going to run into rarely,
but it can happen-- particularly in multi-device households.
And when it does, you're going to need
to make sure you do the right thing.
So when you load a snapshot from the cloud,
it comes with what you can think of as a version
number, or perhaps a tag.
And this tag identifies the latest version
of your saved game on the cloud that our servers
agree is legit.
We sometimes refer to this as the base version.
Now, you can't alter this tag or even view it.
It is hidden from you.
But when you save the game back to Google servers using
commit and close, this old tag gets sent back
to the cloud along with the newer version of your snapshot.
Now, when Google servers receive this snapshot,
they check and see-- is this tag the same
as what it's recorded as the last base
version of your snapshot?
If so, then it knows that nobody else has gone ahead and messed
with your saved game in between the time that you first
opened it and when you're committing it now.
And if that's the case-- and 90% of the time,
it will be-- your saved game is updated
and the tag is changed to reflect the new base version.
So now, remember earlier when I said
that as soon as you submit a snapshot, it's closed
and you have to reopen it again if you
want to do anything with it?
Well, one of things you're doing when
you reopen this newly-submitted snapshot
is getting this newest version of the tag from our servers.
So now, let's go back to our earlier example here.
And I'm going to go ahead and introduce a conflict
by adding a second device.
And they're both going to open this version of my saved game,
which we have tagged as version A. Now on my first device,
I'm going to go ahead and make a change.
Let's spend 80 gold here and get myself a better sword.
And now, I'm going to save this came back to the cloud.
Google servers are going to look at the tag passed along
with this snapshot and confirm that-- yes, it
does match the last base version tag it had recorded earlier.
Everything looks good.
So my server accepts this new snapshot,
re-tags it as version B. And if I
were to reopen this saved game on this first device,
it would know that it's now working off of this version B.
But let's not do that.
Instead, let's go to my second device.
Remember, this device had last opened
version saved game A. Right?
Well, with that saved game, I'm going to spend my gold
and buy a helmet instead.
And now, I can save this version to the cloud, too.
Now, the servers are going to see that this snapshot is based
on version A, but it also knows that version B is the latest
base version on the cloud, so it's not
going to be happy here.
But here's the thing you might not expect.
It's not going to reject the saved game.
It's just going to make a note that something is funny,
and it's going to save this newest saved game--
we'll call it version A*-- alongside version B.
Why do this, by the way?
Why not just reject a conflicting snapshot
when you're trying to save it?
Well, there are few reasons.
One is that this approach tends to better handle the situation
where a user is trying to save and load
the game while the device is offline.
Another reason is that Android devices,
through some clever caching of network calls,
might not actually save your game on the network
while your application is running.
It might do it a little bit later
when your application is in the background.
And if you want to do something like ask the user how
to resolve these two saved games,
you really can only do that when your application is running--
which generally happens when you're
trying to open the snapshot instead.
So point being this conflict doesn't
get resolved until the user actually
tries to open their last saved game.
And at this point, the server says-- oh, hey wait a minute.
I have this version B, which is the last saved base
version I've declared legit.
But I've also got this A* version now.
What do you want to do here?
Well, what do you want to do?
What you actually decide to do depends a little bit
on your platform, and is kind of up to you.
On iOS, you have a number of options, one of which
is to let the library figure it out
for you by declaring a snapshot merge policy.
If you're to go with GPG snapshot policy BaseWins,
you're basically telling the library
to always accept the latest base version of the snapshot
that the server has accepted and throw away any conflicts.
So in this case, version B would win.
If you go with snapshot policy RemoteWins,
you would accept the conflict version instead.
In this case that's A*.
And if you're saying to yourself-- wait, that's crazy.
Why would I ever use the conflict version?
Remember that, in our example anyway,
A* is actually the most recent saved game.
So this method could make sense.
But frankly, I think both of these strategies
are pretty blunt instruments without a lot of nuance.
One that I like better is snapshot policy
LongestPlaytime.
Here, you're asking the library to look
at the Playtime metadata of each of these two snapshots,
and pick the snapshot where you've
recorded that the user has played the longest.
This could be a smart way to handle conflicts,
particularly if you're trying to resolve two conflicts where
one of the saved games is one where the user has just
played on a plane for four hours.
Right?
You don't want to lose those four hours of game play.
Note, however, that this is all relying on the time played
metadata that is being generated by you, the game developer.
So it's only as good as the data you put in.
And then finally, there's GPG snapshot policy Manual.
In this case, you're given both snapshots, which you're
allowed to open and read fully, and then you
can decide exactly how to proceed-- either
by picking one of these two snapshots,
or by creating a brand new snapshot entirely.
In some cases, if you're doing the manual merge--
in spite of the name manual, you might actually
be able to merge these two saved snapshots automatically using
your own games logic.
This works best in games where the stats tend
to be ones you can kind of always union
together in a-- hey, let's just take the highest or lowest
number scenario.
For example, if I have a game, maybe a puzzle game
where the only thing I'm recording
are the high scores and the number of stars
are in the various levels.
My game could probably merge these two saves automatically,
just using some simple-- you know what?
We'll take the level with the highest score kinds of rules.
And this would work out nicely.
In many other cases, though, such as our example where
the user has purchased a helmet in one situation and a sword
in the other, I'm not quite sure how we could merge these two
programmatically-- unless there was a sword
you wore on your head or something.
But that would be dumb.
So the best scenario here might simply
be to bring up a dialog box, and ask
the user which saved game they want to keep.
Remember, at this point you have access to the snapshot metadata
as well as the actual game data, if you want.
So you could populate this dialogue
with whatever information your game needs
to make sense to your user.
How ever you do it, once you pick the version to commit,
this will be saved back up to the server.
The server will update this snapshot to version C,
and then it will close it.
Which means-- yes, that's right-- you
will need to reopen it again to proceed.
Now, on the Android side of things,
your choices are a little bit more limited.
You have no fancy automatic merge policies or anything.
You basically just have to pick one of these two snapshots
to move forward with and that's it.
Now, to help you do this, you can retrieve both snapshots
within the code.
What the code refers to here as the snapshot
is the base snapshot-- where we've
been calling version B in our example.
And what the code refers to here as the conflicting snapshot is
what we've been calling version A* in our example.
And you can recreate most of those iOS
merge policies with just a few lines of code.
For instance, we can always choose
to use the base snapshot.
We could always use the remote version or the conflict
snapshot.
Or we could always take the snapshot
with a long display time.
And if you wanted to go with-- hey, let's show a user
a dialog box with both saved versions,
ask which one they want to move forward
with-- you could totally do that, too.
I just didn't include the code here.
The only tricky part will be if we
want to do a manual merge, where the new snapshot is
a fancy combination of the two existing ones.
So remember on Android, you have to just pick
one of these two snapshots to move forward with.
So what we need to do is pick one of our snapshots,
save that up as our new save.
Then we could immediately reopen it,
apply all the changes we want to turn it
into our fancy auto merged option.
And then send it up and commit it again.
And in case you're curious, this is actually
what the iOS version of the library does behind the scenes
if you resolve a conflict with the third snapshot.
It just kind of takes care of this work for you,
because it's helpful like that.
On the C++ side, you have a mix of the two libraries.
We do let you pick from a number of auto-resolve strategies
like we do on iOS.
But if you want to manually resolve
two conflicting snapshots with a third,
you're first going to have to pick one, commit it, then
reopen it, apply the new changes,
commit it again like you would do in the Android Java SDK.
Either way, once you have resolved and committed
your new snapshot, you should now
have the confidence to reopen your saved game, knowing
that everything is right with the world.
Right?
Well, kind of.
In some rare situations, you might end up in cases
where you have more than one conflict version sitting out
there on the cloud.
Typically, this happens when a player is playing their game
on three or more devices at once.
Rare, definitely.
But it can happen.
Now in these situations, when you ask to open a snapshot,
you're given the base version and a conflict version
to resolve-- just like normal.
And just like normal, you send a resolved version
back up to the server, the tag is updated,
and then the snapshot is closed.
Well, our game still wants to use this snapshot.
So it's going to ask the server to open it.
And that's when the server tells us-- oh hey, wait.
You actually need to resolve this new base snapshot
with his other version I've also had hanging around.
But that's no problem for us.
Right?
We're still just resolving two snapshots.
This is all the same logic we've been applying before.
So we simply resolve this pair of snapshots,
commit the correct version back to the cloud where
it once again gets closed, and then we can finally reopen it.
And there was much rejoicing.
Now in general, this whole idea of having
more than one conflict at once shouldn't throw you off
too much.
After all, we're really just reapplying
a lot of the same logic we've already written.
I think the area where it can get a little tricky is
if you're an Android, Java, or C++ developer
and you're waiting to apply your fancy custom merge snapshot
on top of an existing snapshot you've just committed.
You're probably expecting to get a single snapshot back
after your previous commit and reopen.
But instead, you get back two, which might confuse your game.
So what you do at this point depends a little
on your game's logic, what's in your fancy combo
snapshot, what's in this new remote snapshot.
But again, it's really just an extension of code
you've probably already written.
You just need to be aware that this situation can
happen and code defensively.
All right.
That was a lot of detail, but I'm
hoping at this point you have a better understanding of what's
going on underneath the hood when you start implementing
saved games for your game.
Don't worry if any of this seemed confusing for you.
We have sample code and apps and guides for you to look at.
And of course, you can always re-watch this video anytime you
want.
If I were you, I'd bookmark this video
and then watch it again when you're actually
trying to implement this stuff, because probably
some of these edge cases will make a little more sense.
So once again, thank you very much for watching.
Feel free to follow me on G+ for serious work-related stuff
and Twitter for mostly dumb, jokey stuff.
And otherwise, I will see you back here in YouTube land
at some point in the near future.
Later, skaters.
Do people still say that these days?
Seacrest out.
You know what?
I'll just see you soon.
Bye.