1

我正在开发一款竞技回合制游戏。当匿名访问者访问一个页面时,他会自动订阅一个非完整的游戏实例,然后他可以观察或加入该动作。每个游戏实例中的点数有限。加入游戏(占用一个位置)时,旧订阅将停止并创建新订阅,其中还包括基于他选择的位置的私人信息。到目前为止,一切都很好。

现在我想在玩家没有在合理的时间内完成他的回合时让服务器腾出一个位置。

问题是我如何确保被踢出位置的玩家不再收到现在打算发送给占据他位置的其他人的更新?显然,这必须发生在服务器端,因为客户端不能被信任。理想情况下,被踢出的用户会再次无缝地成为观察者。

我知道有一个可以在publish()内部调用的方法stop( ) ,但是当Meteor.setTimeout()设置的回调在服务器上被调用时,如何使用它来停止一个特定客户端的已发布订阅?

我正在尝试做的一些经过大量修改的代码(不是为了工作,只是为了给你一个想法)

  if Meteor.isClient
      publicGameHandle = Meteor.subscribe 'GameInstances'

      join = (gameInstanceId, spot) ->
          Meteor.call "join", gameInstanceId, spot, (err, guestId) ->
              Session.set("guestId", guestId)
              privateGameHandle = Meteor.subscribe 'GameInstances',
              gameInstanceId, spot, guestId, ->
                  publicGameHandle.stop()

   if Meteor.isServer
       privateSubscriptions = {}

       Meteor.publish 'GameInstances', (gameInstanceId, spot, guestId) ->
           if gameInstanceId
               GameInstances.find {_id: gameInstanceId}
               privateSubscriptions[guestId] = @
           else
               secretFields = {spots.guestId:false, spots.privateGameInfo:false}
               GameInstances.find {openSpots: {$gt: 0}}, {fields: secretFields}

       Meteor.methods({
           join: (gameInstanceId, spot) ->
               guestId = Random.id()
                   gameInstances[gameInstanceId].addPlayer(spot, guestId)
               guestId

           completePlayerTurn: (gameInstanceId, spot, guestId) ->
               gameInstance = gameInstances[gameInstanceId]
               Meteor.clearTimeout(gameInstance.timer)
               nextPlayer = gameInstance.getNextPlayer()

               kick = () ->
                   privateSubcriptions[nextPlayer.guestId].stop()
                   gameInstance.removePlayer(nextPlayer.guestId)
               gameinstance.timer = Meteor.setTimeout(kick, 60000)
4

1 回答 1

1

创建两个单独的订阅,它们都返回游戏数据。一个订阅应返回公共游戏数据,另一个应返回当前用户可查看的游戏数据。它们将在客户端合并到一个集合中。用户加入的游戏将包含私人信息和公共信息,他们未加入的游戏将仅包含公共信息。然后在客户端,Game.findOne(thisGame.id)当玩家加入游戏时,liveQuery like 会自动接收私人信息,并导致您的模板重新渲染等。

用于说明目的的伪代码:

if Meteor.isClient
  Meteor.subscribe 'GameInstances'
  Meteor.subscribe 'MyGameInstances', guestId

  join = (gameInstanceId, spot) ->
      Meteor.call "join", gameInstanceId, spot, (err, guestId) ->
          Session.set("guestId", guestId)

if Meteor.isServer

   Meteor.publish 'GameInstances', () ->
       secretFields = {spots.guestId:false, spots.privateGameInfo:false}
       GameInstances.find {openSpots: {$gt: 0}}, {fields: secretFields}

   Meteor.publish 'MyGameInstances', (guestId) ->
       GameInstances.find {'spots.guestId': guestId}, {fields: secretFields}

   Meteor.methods({
       join: (gameInstanceId, spot) ->
           guestId = Random.id()
           gameInstances[gameInstanceId].addPlayer(spot, guestId)
           guestId

       completePlayerTurn: (gameInstanceId, spot, guestId) ->
           gameInstance = gameInstances[gameInstanceId]
           Meteor.clearTimeout(gameInstance.timer)
           nextPlayer = gameInstance.getNextPlayer()

           kick = () ->
               gameInstance.removePlayer(nextPlayer.guestId)
           gameinstance.timer = Meteor.setTimeout(kick, 60000)

听起来您可能会在一个游戏实例上包含许多玩家的私人信息。所以这将更难过滤允许的字段(并且只发布当前客人的私人信息,而不发布其他玩家的私人信息)。

在这种情况下,您可以考虑:

  • 将私有信息重构为单独的集合,并将其作为单独的订阅发布
  • 自定义发布功能。这是一个例子:

https://gist.github.com/colllin/8321227

在这段代码中,我正在观察一个集合并发布将合并到Meteor.users集合中的数据。您可以看到订阅正在发布类似的数据{_id: userId, wins: 25},这使得wins客户端的用户模型上的字段可用。您可能想要observe()Games 集合,但根据当前用户手动过滤私有数据,然后将该私有数据发布到 Games 集合以与公共数据合并。您可以guestId像上面那样将 传递给发布功能。

于 2014-01-08T18:07:48.250 回答