1

I have tried both FireBase and PubNub to create this simple multiplayer game. Created with only two players. One big(and justified) concern is conflicting users. Let me explain :

each "game" constructed with just two players(not more). If 4 players logs as the same time. And each player search for a "match". player one might match with player two.while player two might match with player three, and so on.

How can i avoid it, and guarantee that each player will get a single and unique match?, or in other words , prevent matching one user with more than other one

Roi Mulia
  • 5,626
  • 11
  • 54
  • 105
  • Hey @Thomas . Can you please expand? Plus. keep in mind that each player seek for a match as well. so player 1 could find 2 , while at the same time player 2 could find player 3 – Roi Mulia Jan 08 '16 at 10:29
  • @Thomas Actually i thought about something like that, the problem starts when(like you said) people leaving\not responding to a game. So in case user exist in queue but not responding, what is the right approach? – Roi Mulia Jan 08 '16 at 10:35
  • @Thomas , Alright, And how could we know if the player is "AFK" but online? So for example, You are online are searching for a match, so the system assumes you are OKAY for playing. I send you propose to play, should i handle some kind of timer to check your respond? if not, remove/move you to the end of the line? – Roi Mulia Jan 08 '16 at 10:40
  • @Thomas , Alright. Looking forward – Roi Mulia Jan 08 '16 at 10:43
  • See http://stackoverflow.com/questions/33737457/android-correctly-pairing-and-connecting-two-users-in-a-random-chat-app-using for similar question/answer using PubNub. [Contact PubNub Support](https://www.pubnub.com/support/) if you need to talk the details a bit further. – Craig Conover Jan 21 '16 at 18:27

2 Answers2

3

With Firebase, security rules and transactions would be the key to an elegant solution.

If you're willing to set up a node.js script or other server-side worker, this is fairly straightforward. Players would write to a "lobby" when they want a match. The server script would perform the matches and write back the "game room" they are going to join. The structure would be basically thus:

 /games/$game_id/users/user1/<user_id>
 /games/$game_id/users/user2/<user_id>

 /lobby/$user_id/false    (an unmatched user)
 /lobby/$user_id/$game_id (a matched user)

Now clients would simply write to the lobby when they want to join a game, and then wait for the server to assign them a game id:

var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
var lobbyRef = ref.child('lobby/' + <my user id>);
lobbyRef.set(false); // I want to join a game
lobbyRef.on('value', function(snap) {
   if( snap.val() !== null ) {
      console.log('I joined a game!', snap.val());
   }
});

The server is nearly as simple. Assuming node.js:

var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
var lobbyRef = ref.child('lobby');
var gamesRef = ref.child('games');
var player1 = null;

// listen for requests to join
lobbyRef.on('child_added', function(snap) {
   // assign as player1 if nobody is waiting
   if( !player1 ) {
      player1 = snap.ref();
   }
   // if someone is already waiting, assign both players a room
   else {
      var player2 = snap.ref();
      var gameRef = gamesRef.push({ 
         players: {
            player1: player1.key(),
            player2: snap.key()
         }
      }, function(err) {
        if( err ) { throw err; } // a bug
        // let the players know they have been matched and which room to join
        player1.set(gameRef.key());
        player2.set(gameRef.key());
      });
   }
});

Obviously there is some work to make all this fault tolerant and security rules would be needed to prevent cheating.

Doing this entirely on the client is slightly more involved, but certainly manageable.

Have each player attempt to match themselves to anybody in the lobby. If nobody is in the lobby, then wait there. This is done with a transaction to prevent conflicts.

var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
var lobbyRef = ref.child('lobby');

function waitInLobby() {
   lobbyRef.once('value', lobbyUpdated);
}

function lobbyUpdated(snap) {
   if( snap.val() === null ) {
      // lobby is empty, so join and wait
      var ref = lobbyRef.child('<my user id>').push(false);
      ref.on('value', someoneMatchedMe);

      function someoneMatchedMe(snap) {
         if( snap.val() !== false ) {
            console.log('I got matched in room', snap.val());
            ref.off('value', someoneMatchedMe); // stop monitoring
            snap.ref().remove(); // leave the lobby
         }
      }
   }
   else {
      // lobby is not empty, so try to match someone
      var possibles = [];
      snap.forEach(function(ss) {
         possibles.push(ss.ref());
      });
   }
}

function matchUser(possibles) {
   if( !possibles.length ) { waitInLobby(); }
   var opponentRef = lobbyRef.child(possibles.shift());
   opponentRef.transaction(function(currentVal) {
      if( currentVal !== false ) {
         // already claimed, start over
         matchUser(possibles);
      }
   });
}

Some security rules would be critical here, in addition to the transactions. There is also plenty of room for optimization, but at the point that you're optimizing for production, that's a job for your engineering team, rather than a Stack Overflow contributor.

Kato
  • 40,352
  • 6
  • 119
  • 149
  • Hey Kato! Thank for the well detailed answer(im currently away from my computer, i will dive into it as soon as I can), much appreciate it. Mind if i direct you to slightly similar post i har posted earlier? Would like you to examine the solution as well, http://stackoverflow.com/a/34681063/4156957 – Roi Mulia Jan 08 '16 at 23:24
2

matching

[p1] - [p2] - [p3] - [p4] - [p5] - etc...

Ok so you match odd numbered player (N) with the next even numbered player (N + 1).

Of course P5 stays alone and should wait for the next round, make him P1 for that round. That way he never has to wait 2 rounds

You can create a tuple for the pairs, but I would make the Player class also have a field oponent of type Player

edit1: You keep track of the raw queue in a regular array of Players. As soon as the array reaches it's desired size you trigger the above algorithm which stops the ability to change to current player pool and all matches will be definitive.

idle check

Ok so you let players enter the queue and you display a timer / empty slot counter so they have feedback how long they have to wait.

As soon as the match starts you let them lock in (League of Legends does it this way as well) If 1 or more players do not lock in you start the queue process over, maybe with a decreased timer so the players don't have to wait to long.

If you make this time based (not slot based) then if 1 players does not respond (let's say P2) you move the last player (P5) to his slot (P5 is now P2) and everyone can play.

If you have more questions I will edit this answer.

online Thomas
  • 8,864
  • 6
  • 44
  • 85
  • the question is how do i create the queue? I'm using FireBase and PubNub, as far as i know i have no access to the server side. What would you recommend? (if you ever used them) – Roi Mulia Jan 08 '16 at 10:50
  • 1
    I will definitely try this(i hope that's its possible within the SDK's capabilities), I will let you know if i encountered more issues. Thanks! – Roi Mulia Jan 08 '16 at 11:04