如何从Meteor服务器端代码管理和有选择地停止已发布的订阅

时间:2013-08-15 14:48:35

标签: meteor

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

现在我想让玩家在合理的时间内没有完成转弯的时候释放一个位置。

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

我知道有一个方法 stop()可以在 publish()中调用,但是如何使用它来停止某个特定客户端的已发布订阅在服务器上调用 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)

1 个答案:

答案 0 :(得分:1)

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

用于说明目的的伪代码:

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)

听起来你可能会在一个游戏实例中包含许多玩家私人信息。因此,更难过滤允许的字段(并且只发布CURRENT来宾的私人信息,而不发布其他玩家的私人信息)。

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

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

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

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