Syncing part of state / design paradigm

Hi, I am experimenting with this library and have a setup like this (JS, a lot omitted for brevity):

class MyRoom extends Room {
  onInit () {
     this.setState({ players: {} })
     this.tick = this.tick.bind(this)
     this.setSimulationInterval(this.tick, 1000 / 30)
  }
  onJoin (client, options) {
    this.state.players[client.sessionId] = new Player(sessionId, this)
  }
  tick () {
    Object.values(this.state.players).forEach(player => player.update())
  }
}

class Player {
  constructor (id, game) {
     this.id = id
     this.game = game // <-- this is the main problem
     this.x = randX()
     this.y = randY()
     this.update = this.update.bind(this)
  }
  update () {
     // perform some calculations and update this.x and this.y
     // this involves this.game (for collision detections against other players, for example)
  }
}

As you can see, the room has a game loop which calls Player#update every frame (this just moves the player according to his velocity vector and checks collisions against walls and other players via this.game).

The problem is that this.game puts game on the player state object which is transmitted to the clients, as well as the update function. (This actually crashes the process because it cannot format it correctly). Using nosync also doesn't work because then for some reason this.game is not available in Player#update.

Ideally I could have a this.state field on Player with the actual state, and the rest left as variables for use in the class (similar to how the room works), ie:

class Player {
   constructor (id, game) {
      this.id = id
      this.game = game
      this.state = { x: randX(), y: randY() }
   }
}

What's the recommended approach here?

Hi @zpr, welcome!

I think the problem you're facing is exactly what @Nebri just asked about in the Gitter channel.

You're probably facing the famous this problem in JavaScript. When referencing the function directly on setSimulationInterval you will lose the this reference.

You can call it like this instead:

this.setSimulationInterval(() => this.tick())

or

this.setSimulationInterval(this.tick.bind(this))

Hi @endel thanks for your response.

Unfortunately that is not the problem, I do have access to this as I have it bound in the constructor:

this.tick = this.tick.bind(this)

Perhaps my post was not clear. The problem is that the nested state object Player has too many fields. That is, the entire Player class is included as part of the state in the room (including the reference to game, and its own internal updating functions).

I found by looking at the source of your tanx game overloading toJSON on the Player class to be very helpful.

Thanks