当"匹配"时避免冲突用户进行多人游戏

时间:2016-01-08 10:23:08

标签: ios swift firebase multiplayer pubnub

我已经尝试过FireBase和PubNub来创建这个简单的多人游戏。仅使用 两名玩家创建。一个大的(合理的)问题是用户冲突。让我解释一下:

每个游戏"仅由两名球员(不是更多)组成。如果4名玩家同时登录。每个玩家都会搜索"匹配"。玩家一可能与玩家二匹配。而玩家二可能与玩家三匹配,依此类推。

我如何避免它,并保证每个玩家都能得到一个独特的匹配?或换句话说,阻止一个用户与其他用户匹配

2 个答案:

答案 0 :(得分:3)

借助Firebase,security rulestransactions将成为优雅解决方案的关键。

如果您愿意设置node.js脚本或其他服务器端工作程序,这非常简单。当玩家想要比赛时,他们会写一个“大厅”。服务器脚本将执行匹配并回写他们将要加入的“游戏室”。结构基本上是这样的:

 /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)

现在,当客户想要加入游戏时,客户只需写入大厅,然后等待服务器为他们分配游戏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());
   }
});

服务器几乎一样简单。假设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());
      });
   }
});

显然,有一些工作可以使所有这些容错,并且需要security rules来防止作弊。

完全在客户端上执行此操作稍微复杂一些,但当然可以管理。

让每个玩家尝试将自己与大厅中的任何人匹配。如果没有人在大厅,那么等一下。这是通过transaction完成的,以防止发生冲突。

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);
      }
   });
}

除了交易之外,一些安全规则在这里至关重要。还有很多优化空间,但是在您优化生产时,这对您的工程团队来说是一项工作,而不是Stack Overflow的贡献者。

答案 1 :(得分:2)

<强>匹配

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

好的,所以你将奇数编号的玩家(N)与下一个偶数编号的玩家(N + 1)匹配。

当然P5保持独立并且应该等待下一轮,让他成为那一轮的P1。这样他就不必等待2轮

您可以为对创建一个元组,但我会让Player类的字段oponent类型为Player

edit1: 您可以在常规的玩家阵列中跟踪原始队列。一旦数组达到所需大小,您就会触发上述算法,该算法将停止更改为当前播放器池的能力,并且所有匹配都将是最终的。

空闲检查

好的,所以你让玩家进入队列并显示一个计时器/空位计数器,这样他们就有feedback等待的时间。

一旦比赛开始,你就让他们lock in(英雄联盟也这样做) 如果一个或多个玩家没有lock in你开始排队过程,可能会减少计时器,这样玩家就不必等待很长时间。

如果你基于这个时间(不是基于时段),那么如果1个玩家没有回应(让我们说P2)你将最后一个玩家(P5)移动到他的位置(P5现在是P2)并且每个人都可以玩。

如果您有更多问题,我会编辑此答案。