docs: function GameRoom(config) {
      var that, node;
  
      Room.call(this, 'Game', config);
      if (!this.logicPath) this.logicPath  = './rooms/game.room';
  
      if (config.treatmentName && 'string' !== typeof config.treatmentName) {
          throw new TypeError('GameRoom: config.treatmentName must be ' +
                              'undefined or string. Found: ' +
                              config.treatmentName);
      }
  
      if (config.runtimeConf && 'object' !== typeof config.runtimeConf) {
          throw new TypeError('GameRoom: config.runtimeConf must be object. ' +
                              'Found: ' + config.runtimeConf);
      }
  
      /**
       * ### GameRoom.treatmentName
       *
       * The name of the treatment played, or 'standard' if not defined
       */
      this.treatmentName = config.treatmentName || 'standard';
  
      if (!this.game.settings[this.treatmentName]) {
          throw new Error('GameRoom: game ' + this.gameName + ' treatment ' +
                          'not found: ' + this.treatmentName);
      }
  
      /**
       * ### GameRoom.gameLevel
       *
       * Game paths for the room
       */
      this.gameLevel = config.gameLevel;
  
      if (this.gameLevel && !this.game.levels[this.gameLevel]) {
          throw new Error('GameRoom: game ' + this.gameName + ' game level ' +
                          'not found: ' + this.gameLevel);
      }
  
      /**
       * ### GameRoom.gamePaths
       *
       * Game paths for the room
       */
      this.clientTypes = this.gameLevel ?
          this.game.levels[this.gameLevel].clientTypes :
          this.game.clientTypes;
  
      /**
       * ### GameRoom.gameTreatment
       *
       * Actual settings for current game based on treatment and runtime conf
       *
       * @see GameRoom.runtimeConf
       * @see GameRoom.setupGame
       */
      this.gameTreatment = this.game.settings[this.treatmentName];
      if (this.runtimeConf) J.mixin(this.gameTreatment, this.runtimeConf);
  
      /**
       * ### GameRoom.dataDir
       *
       * Path to the data directory
       *
       * @see ServerChannel.getGameDir
       */
      if (config.ownDataDir || this.channel.roomOwnDataDir) {
          this.dataDir = path.resolve(this.channel.getGameDir(),
                                      'data', this.name);
  
  
          // TODO: warning if it already exists.
          fs.ensureDir(this.dataDir);
      }
      else {
          this.dataDir = path.resolve(this.channel.getGameDir(), 'data');
      }
      this.dataDir += '/';
  
      // Configure disconnect/reconnect policy.
  
      that = this;
      node = this.node;
  
      /**
       * ## GameRoom.wrongNumOfPlayers
       *
       * Default listener for `onWronPlayerNum` on logics
       *
       * This listener is added as default stager property `onWrongPlayerNum`
       * to all `logic` client types by method `GameRoom.getClientType`,
       * if no other is already defined.
       *
       * The listener is invoked when there is a "wrong" number of players.
       * The number of players is "wrong" if it has been defined any
       * min|max|exactPlayers property in current game step.
       *
       * Default behavior:
       *
       * 1 - Immediately pauses all connected players. A message is displayed
       *     with information.
       * 2 - A timer is started for the value specified in settings.WAIT_TIME,
       *     or 30 seconds otherwise.
       * 3 - If timer expires, game is resumed and, if a custom callback is
       *     is defined, it is executed.
       *
       * The listener is then invoked with `node.game` context.
       *
       * @param {string} type The type of "wrong" number: 'min', 'max', 'exact'
       * @param {function} customCb Optional. A callback to be executed in
       *     in case the timer expires.
       * @param {object} player Optional. The player object that triggered
       *     the listener by either connecting or disconnecting (if available)
       *
       * @see GameRoom.correctNumOfPlayersAgain
       * @see GameRoom.getClientType
       */
      this.wrongNumOfPlayers = function(type, customCb, player) {
          var node;
          var waitTime, msgToClients;
          node = this.node;
  
          // If the default value (30) is changed the documentation
          // on the wiki needs to be updated too (Disconnections page).
          waitTime = 'number' === typeof this.settings.WAIT_TIME ?
              this.settings.WAIT_TIME : 30;
  
          if (type === 'min') {
  
              msgToClients = this.settings.WAIT_TIME_TEXT ||
                  'One or more players disconnected. We are waiting ' +
                  '<span id="ng_pause_timer">' + waitTime +
                  '</span> seconds to see if they reconnect.';
  
              node.warn('warning: not enough players!!');
          }
          else if (type === 'max') {
              msgToClients = this.settings.WAIT_TIME_TEXT ||
                  'Too many players connected. We are waiting ' +
                  '<span id="ng_pause_timer">' + waitTime + '</span> ' +
                  'seconds to see if the right number of players is restored.';
  
              node.warn('warning: too many players!!');
          }
          else {
              msgToClients = this.settings.WAIT_TIME_TEXT ||
                  'Wrong number of connected players detected. We are waiting' +
                  '<span id="ng_pause_timer">' + waitTime + '</span> ' +
                  'seconds to see if the right number of players is restored.';
  
              node.warn('warning: wrong number of players!!');
          }
  
          if (!node.game.isPaused()) {
              // Pause connected players.
              node.remoteCommand('pause', 'ROOM', msgToClients);
              node.game.pause();
          }
  
          that.countdown = setTimeout(function() {
              node.warn('Disconnection Countdown fired: ' + node.nodename);
              node.remoteCommand('resume', 'ROOM');
              node.game.resume();
              if (customCb) customCb.call(node.game, player);
              setTimeout(function() {
                  if (node.game.shouldStep()) node.game.step();
              }, 100);
  
          }, waitTime * 1000);
      };
  
      /**
       * ## GameRoom.correctNumOfPlayersAgain
       *
       * Listener invoked at any time the correct number of players is recovered
       *
       * The number of players is "wrong" if it has been defined any
       * min|max|exactPlayers property in current game step.
       *
       * Default behavior:
       *
       * 1 - Immediately resumes all connected players that have been
       *     paused by callback `wrongNumOfPlayers`
       * 2 - The timer defined by `wrongNumOfPlayers` is cleared
       * 3 - If any custom callback was defined, it gets executed
       *
       * This listener is added as default stager property `onCorrectPlayerNum`
       * to all `logic` client types by method `GameRoom.getClientType`,
       * if no othe is already defined.
       *
       * The listener is then invoked with `node.game` context.
       *
       * @param {string} type The type of "wrong" number: 'min', 'max', 'exact'
       * @param {function} customCb Optional. A callback to be executed in
       *     in case the timer expires.
       * @param {object} player Optional. The player object that triggered
       *     the listener by either connecting or disconnecting (if available)
       *
       * @see GameRoom.wrongNumOfPlayers
       * @see GameRoom.getClientType
       */
      this.correctNumOfPlayersAgain = function(type, customCb, player) {
          var node;
          node = this.node;
          if (type === 'min') {
              node.warn('warning: enough players again!!');
          }
          else if (type === 'max') {
              node.warn('warning: number of players below maximum again!!');
          }
          else {
              node.warn('warning: correct number of players again!!');
          }
  
          // Delete countdown to terminate the game.
          clearTimeout(that.countdown);
          that.countdown = null;
  
          // Player can reconnect during the countdown or after.
          // If after, then the game won't be paused.
          if (node.game.isPaused()) {
              node.game.resume();
              // Resume other players.
              setTimeout(function() {
                  node.game.pl.each(function(p) {
                      if (player.id !== p.id) {
                          node.remoteCommand('resume', p.id);
                      }
                  });
              }, 100);
          }
  
          if (customCb) customCb.call(node.game, player);
      };
  
      // Logs connections.
      node.on.pconnect(function(p) {
          that.logClientEvent(p, 'connect');
      });
  
      // Some players are already there, and do not fire pconnect.
      node.once('INIT', function(p) {
          // Avoid the whole loop.
          if (!that.logClients) return;
          node.game.pl.each(function(p) {
              that.logClientEvent(p, 'connect');
          });
  
      });
  
      // Logs disconnection.
      node.on.pdisconnect(function(p) {
          console.log('Disconnection: ' + p.id + ' (server stage: ' +
                      node.player.stage + ')');
          that.logClientEvent(p, 'disconnect');
  
      });
  
      // Player reconnecting.
      // This function is called first, and it invokes any
      // min/max/exact handler accordingly.
      node.on.preconnect(function(p) {
          var code, discoStage, discoStageLevel, curStage, reconCb, res;
          var compareSteps, reconOptions;
          var milliseconds, resetTime;
  
          that.logClientEvent(p, 'reconnect');
  
          node.warn('Oh...somebody reconnected!', p);
          code = that.channel.registry.getClient(p.id);
  
          discoStage = code.disconnectedStage;
          curStage = node.game.getCurrentGameStage();
          compareSteps = GameStage.compare(discoStage, curStage);
  
          // Check if only within stage reconnections are allowed.
          if (that.sameStepReconnectionOnly && compareSteps) {
              node.err('GameRoom: player reconnected from another step. ' +
                       'Not allowed. Player: ' + p.id);
              disposeClient(node, p);
              return;
          }
  
          // Setup client.
          that.setupClient(p.id);
  
          if (code.lang.name !== 'English') {
              // If lang is different from Eng, remote setup it.
              // TRUE: sets also the URI prefix.
              node.remoteSetup('lang', p.id, [code.lang, true]);
          }
  
          // Start the game on the reconnecting client.
          // Need to give step: false, we just init it.
          node.remoteCommand('start', p.id, { step: false });
  
          // Create the reconnection options object.
          reconOptions = { plot: {} };
  
          // Mark the state as DONE, if this is the case.
          // Important!
          // IF the client was DONE, and waiting for other players,
          // and the STEP command is sent right before its disconnection,
          // THEN the willBeDone option is applied to the next step, which
          // is the wrong thing to do. So we add willBeDone only on same
          // stage, or if the client is ahead.
          discoStageLevel = code.disconnectedStageLevel;
          if (compareSteps <= 0 && discoStageLevel === DONE) {
              reconOptions.willBeDone = true;
              // Temporarily remove the done callback.
              reconOptions.plot.done = null;
              reconOptions.plot.autoSet = null;
          }
  
          // Sets the timer on reconnecting client if milliseconds is found.
          milliseconds = node.game.timer.milliseconds;
          if ('number' === typeof milliseconds) {
              // Time left to play in stage.
              resetTime = Math.max(milliseconds -
                                   node.timer.getTimeSince('step', true), 0);
  
              reconOptions.plot.timer = resetTime;
          }
  
          reconCb = this.plot.getProperty(curStage, 'reconnect');
  
          if (reconCb) {
              // Res contains the reconnect options for the step,
              // or false to abort reconnection.
              res = reconCb.call(this, code, reconOptions);
              if (res === false) {
                  node.warn('Reconnect Cb returned false');
                  disposeClient(node, p);
                  return;
              }
          }
  
          // Add player to player list.
          node.game.pl.add(p);
  
          // Start the step on reconnecting client.
          // Unless differently specified by the reconnect callback,
          // target step is what is more advanced between the logic step,
          // and the step when the client disconnected.
          if (!reconOptions.targetStep) {
              // console.log(compareSteps, curStage, code.stage);
              // -1 means first param is ahead in game stage.
              // compareSteps = GameStage.compare(code.stage, curStage);
              if (compareSteps < 0) reconOptions.targetStep = discoStage;
              else reconOptions.targetStep = curStage;
          }
  
          node.remoteCommand('goto_step', p.id, reconOptions);
  
          // See if we have enough players now.
          res = node.game.sizeManager.changeHandler('pconnect', p);
  
          // If we are still missing more players pause reconnecting player.
          if (!res) node.remoteCommand('pause', p.id);
  
      });
  
      this.registerRoom();
  }