Search code examples
meteorcoffeescriptiron-router

Meteor 1.0 - Hosting a lobby and redirecting other users


I have a problem for which I do not know the solution. I am creating game that involves multiple players. One person "hosts" the game and sends "invitations" to other users to join the "lobby". If you've ever played Call of Duty or any similar game, it's the same concept.

Much of this process currently works properly.

I created a collection called Lobby to keep track of all the open and closed lobbies. When a user wants to host a lobby, he clicks on the button, which creates a new lobby and redirects the user to the proper url:

Template.lobby.events
  'click button#host-lobby': (e,t) ->
    lobbyId = Lobby.insert
      host: Meteor.user()._id
      hostName: Meteor.user().username
      status: true
      players: []
    Router.go("currentLobby", _id: lobbyId)

Then, the user can invite other users to the lobby (url) through a modal, which adds a notification object to the invited user's profile. Probably not the best way to do it, so I'm open to suggestions on this front.

Template._playerItem.events
  'click button': (e,t) ->
    lobbyId = Session.get "currentLobby"
    Meteor.call "sendNotification", @_id, lobbyId, Meteor.user().username
    Lobby.update
      _id: lobbyId
    ,
      $addToSet:
        invitedPlayers: @_id

And the method:

Meteor.methods
  sendNotification: (userId, lobbyId, hostName) ->
    sendTo = Meteor.users.findOne(_id: userId)
    Meteor.users.update
      _id: userId
    ,
      $push:
        invite:
          hostName: hostName
          lobbyId: lobbyId

So at this point, the user can either accept or decline the invitation. If he accepts, he is routed to the lobby and is added to the players array in the lobby object. The user shows up in the list of players as one would expect.

My issue starts when I attempt to "start" the game. When the button is clicked, the game is created properly and the host (who pressed the button) is routed to the url for the new game:

Template.currentLobby.events
  'click #start-game': (e,t) ->
    playerIds = [@host]
    @players.forEach (player) ->
      playerIds.push(player.id)
    Meteor.call 'createGame', playerIds
    Router.go('home')

The problem is that the other users in the lobby are not redirected. They have access to the game if they manually go to the url, but they aren't taken there. They have no way of knowing that the game has actually started...

One solution is to add a "Game has started" badge, with a link to the game, but I think a more elegant solution is to route all of the users at the current lobby url to the game that was just started.

Is this functionality possible? Is there a better way to host a lobby?

EDIT

Thank you Chet for the solution. This is how I eventually implemented it:

Template.currentLobby.rendered = ->
  @autorun ->
    data = Template.currentData()
    if data.url
      Router.go data.url

@autorun had some context difficulties, so I just used the lobby data. Then, when someone clicks on the "start game" button, the current lobby is updated with the url of the new game (The Meteor.call 'createGame' returns the _id of the new game).

Template.currentLobby.events
  'click #start-game': (e,t) ->
    playerIds = [@host]
    lobbyId = Template.currentData()._id

    @players.forEach (player) ->
      playerIds.push(player.id)

    Meteor.call 'createGame', playerIds, (err, res) ->
      Lobby.update
        _id: lobbyId
      ,
        $set:
          url: "/game/#{res}"

Works like a charm. Thanks!


Solution

  • OK. First, to address your notifications. This is a valid solution. However, you'll probably want to sort them by date! You'll also want to create a separate Notifications collection. Anytime you update a document, the whole thing is send over DDP to the client. Thus any new notification added to the profile will result the entire profile being sent to the client. You'll also want some way of marking a notification as being read so it can be deleted.

    To address you question about redirecting the lobby, create property called url. Initially it is set to false. Once the host is ready to start the game, they set the url property to the url.

    When a user enters a lobby, start an autorun for the redirect.

    Template.lobby.rendered = ->
      @autorun ->
        if game.url
          Router.go(game.url)
    

    You'll have to make sure that the game.url is a reactive datasource. If you pass the game as the data context using iron router, then you should be able to use @data.url but I'm not 100% sure it will be reactive. Just to be safe, try Games.findOne(@data._id) -- that will certainly be reactive.

    EDIT: Just to be clear, try this:

    Template.lobby.rendered = ->
      @autorun ->
        game = Games.findOne(@data._id)
        if game.url
          Router.go(game.url)