I'm working on a Google Play Services Realtime Multiplayer game where I'd like all clients to have a shared seed before the game starts (for determinism when initializing "random" game state).
I was planning on using Room.getCreationTimestamp however upon testing not all players receive the same value. Likewise Room.getCreatorId is not guaranteed to be the same value for auto-match peers.
There is a video Google Play Games: Choosing a specific user that suggests the peers can choose one peer (say, first from the sorted list) to elect to make a decision. I'm concerned that can misbehave if game starts before the maximum number of players have joined and thus a new player joining an active game might disagree about who is boss. I've sketched out some ad-hoc ways to work around this and also looked at a few consensus and leader election algorithms, but needless to say it is not pretty.
Is anyone aware of a simpler/safer way to generate a shared seed?
Edit: here are the values associated with the room from two different connected devices:
Room.getRoomId():
1. ChoKCQj-99-X_xEQAhABGAAg____________ARDhp87Gh-73-Uk
2. ChoKCQj-99-X_xEQAhABGAAg____________ARC7oOT7-oKouRI
Room.getCreationTimestamp():
1. 1431018058986
2. 1431018047097
Room.getCreatorId():
1. p_COGnzsaH7vf5SRAB
2. p_CLug5Pv6gqi5EhAB
Also Room.getDescription()
returns null
in both cases.
The suggestion about RoomId has some promise, but without knowing how the id is generated I'm skeptical taking a prefix is safe going forward.
Edit: I attempted to use the "creator" as an authority, each node can ask the creator for the seed. Unfortunately, in my two node test case each node believes it is the creator.
I had the same issue to solve. Following that video gave me the right indication. The solution, as described in the video, is to order the participants array and let's assume that the lowest participantId gets the right to choose the random seed. Here how I solved the problem:
private boolean amItheKing() {
int numberOfParticipants = participants.size();
String[] participantIds = new String[numberOfParticipants];
for (int i = 0; i < numberOfParticipants; i++) {
participantIds[i] = participants.get(i).getParticipantId();
}
Arrays.sort(participantIds);
return (participantIds[0].equals(myId));//the first in the list decide and broadcast the seed
}
and then use it in the right place:
public void onActivityResult(int requestCode, int responseCode, Intent intent) {
switch (requestCode) {
case RC_WAITING_ROOM:
// we got the result from the "waiting room" UI.
if (responseCode == Activity.RESULT_OK) {
// ready to start playing
Gdx.app.debug(TAG, "Ready to start the game (waiting room returned OK).");
if (amItheKing()) {
int seed = generateSeed();
broadcastSeed(seed);
prepareForStart(seed);
}
} else if (responseCode == GamesActivityResultCodes.RESULT_LEFT_ROOM) {
// player indicated that they want to leave the room
leaveRoom();
} else if (responseCode == Activity.RESULT_CANCELED) {
// Dialog was cancelled (user pressed back key, for instance). In our game,
// this means leaving the room too. In more elaborate games, this could mean
// something else (like minimizing the waiting room UI).
leaveRoom();
}
break;
}
}