'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }

var _Object$assign = _interopDefault(require('babel-runtime/core-js/object/assign'));
var _classCallCheck = _interopDefault(require('babel-runtime/helpers/classCallCheck'));
var _createClass = _interopDefault(require('babel-runtime/helpers/createClass'));
var _defineProperty = _interopDefault(require('babel-runtime/helpers/defineProperty'));
var _debug = _interopDefault(require('debug'));
var _JSON$stringify = _interopDefault(require('babel-runtime/core-js/json/stringify'));
var _Object$values = _interopDefault(require('babel-runtime/core-js/object/values'));
var WebSocket = _interopDefault(require('isomorphic-ws'));
var _typeof = _interopDefault(require('babel-runtime/helpers/typeof'));
var _Object$getPrototypeOf = _interopDefault(require('babel-runtime/core-js/object/get-prototype-of'));
var _possibleConstructorReturn = _interopDefault(require('babel-runtime/helpers/possibleConstructorReturn'));
var _inherits = _interopDefault(require('babel-runtime/helpers/inherits'));
var request = _interopDefault(require('superagent'));
var EventEmitter = _interopDefault(require('eventemitter3'));

/**
 * 节点地区
 * @readonly
 * @enum {number}
 */
var Region = {
  /** 华北节点 */
  NorthChina: 0,
  /** 华东节点 */
  EastChina: 1,
  /** 美国节点 */
  NorthAmerica: 2 };

/**
 * 事件
 * @readonly
 * @enum {string}
 */
var Event = {
  /**
               * 连接成功
               * @event Play#CONNECTED
               */
  CONNECTED: 'connected',
  /**
                           * 连接失败
                           *
                           * @event Play#CONNECT_FAILED
                           * @param {Object} payload
                           * @param {Number} payload.code
                           * @param {String} payload.detail
                           */
  CONNECT_FAILED: 'connectFailed',
  /**
                                    * 断开连接
                                    * @event Play#DISCONNECTED
                                    */
  DISCONNECTED: 'disconnected',
  /**
                                 * 加入到大厅
                                 * @event Play#LOBBY_JOINED
                                 */
  LOBBY_JOINED: 'lobbyJoined',
  /**
                                * 离开大厅
                                * @event Play#LOBBY_LEFT
                                */
  LOBBY_LEFT: 'lobbyLeft',
  /**
                            * 大厅房间列表变化
                            * @event Play#LOBBY_ROOM_LIST_UPDATED
                            */
  LOBBY_ROOM_LIST_UPDATED: 'lobbyRoomListUpdate',
  /**
                                                   * 创建房间成功
                                                   * @event Play#ROOM_CREATED
                                                   */
  ROOM_CREATED: 'roomCreated',
  /**
                                * 创建房间失败
                                * @event Play#ROOM_CREATE_FAILED
                                * @param {Object} payload
                                * @param {Number} payload.code
                                * @param {String} payload.detail
                                */
  ROOM_CREATE_FAILED: 'roomCreateFailed',
  /**
                                           * 加入房间成功
                                           * @event Play#ROOM_JOINED
                                           */
  ROOM_JOINED: 'roomJoined',
  /**
                              * 加入房间失败
                              * @event Play#ROOM_JOIN_FAILED
                              */
  ROOM_JOIN_FAILED: 'roomJoinFailed',
  /**
                                       * 有新玩家加入房间
                                       * @event Play#PLAYER_ROOM_JOINED
                                       * @param {Object} payload
                                       * @param {Player} payload.newPlayer
                                       */
  PLAYER_ROOM_JOINED: 'newPlayerJoinedRoom',
  /**
                                              * 有玩家离开房间
                                              * @event Play#PLAYER_ROOM_LEFT
                                              * @param {Object} payload
                                              * @param {Player} payload.leftPlayer
                                              */
  PLAYER_ROOM_LEFT: 'playerLeftRoom',
  /**
                                       * 玩家活跃属性变化
                                       * @event Play#PLAYER_ACTIVITY_CHANGED
                                       * @param {Object} payload
                                       * @param {Player} payload.player
                                       */
  PLAYER_ACTIVITY_CHANGED: 'playerActivityChanged',
  /**
                                                     * 主机变更
                                                     * @event Play#MASTER_SWITCHED
                                                     * @param {Object} payload
                                                     * @param {Player} payload.newMaster
                                                     */
  MASTER_SWITCHED: 'masterSwitched',
  /**
                                      * 房间「开启 / 关闭」
                                      * @event Play#ROOM_OPEN_CHANGED
                                      * @param {Object} payload
                                      * @param {Boolean} payload.opened
                                      */
  ROOM_OPEN_CHANGED: 'roomOpenChanged',
  /**
                                         * 房间「可见 / 不可见」
                                         * @event Play#ROOM_VISIBLE_CHANGED
                                         * @param {Object} payload
                                         * @param {Boolean} payload.visible
                                         */
  ROOM_VISIBLE_CHANGED: 'roomVisibleChanged',
  /**
                                               * 离开房间
                                               * @event Play#ROOM_LEFT
                                               */
  ROOM_LEFT: 'roomLeft',
  /**
                          * 房间自定义属性变化
                          * @event Play#ROOM_CUSTOM_PROPERTIES_CHANGED
                          * @param {Object} payload
                          * @param {Object} payload.changedProps
                          */
  ROOM_CUSTOM_PROPERTIES_CHANGED: 'roomCustomPropertiesChanged',
  /**
                                                                  * 玩家自定义属性变化
                                                                  * @event Play#PLAYER_CUSTOM_PROPERTIES_CHANGED
                                                                  * @param {Object} payload
                                                                  * @param {Player} payload.player
                                                                  * @param {Object} payload.changedProps
                                                                  */
  PLAYER_CUSTOM_PROPERTIES_CHANGED: 'playerCustomPropertiesChanged',
  /**
                                                                      * 自定义事件
                                                                      * @event Play#CUSTOM_EVENT
                                                                      * @param {Object} payload
                                                                      * @param {Number|String} payload.eventId
                                                                      * @param {Object} payload.eventData
                                                                      * @param {Number} payload.senderId
                                                                      */
  CUSTOM_EVENT: 'customEvent',
  /**
                                * 错误事件
                                * @event Play#ERROR
                                * @param {Object} payload
                                * @param {Number} payload.code
                                * @param {String} payload.detail
                                */
  ERROR: 'error' };

/**
                                                                                                                                                                                                   * 玩家类
                                                                                                                                                                                                   */var
Player = function () {
  function Player(play) {_classCallCheck(this, Player);
    this._play = play;
    this._userId = '';
    this._actorId = -1;
  }_createClass(Player, [{ key: '_initWithJSONObject', value: function _initWithJSONObject(







    playerJSONObject) {
      this._userId = playerJSONObject.pid;
      this._actorId = playerJSONObject.actorId;
      if (playerJSONObject.attr) {
        this.properties = playerJSONObject.attr;
      } else {
        this.properties = {};
      }
    }

    /**
       * 玩家 ID
       * @type {string}
       * @readonly
       */ }, { key: 'isLocal',













    /**
                                * 判断是不是当前客户端玩家
                                * @return {Boolean}
                                */value: function isLocal()
    {
      return (
        this._actorId !== -1 && this._play._player._actorId === this._actorId);

    }

    /**
       * 判断是不是主机玩家
       * @return {Boolean}
       */ }, { key: 'isMaster', value: function isMaster()
    {
      return this._actorId !== -1 && this._play._room.masterId === this._actorId;
    }

    /**
       * 判断是不是活跃状态
       * @return {Boolean}
       */ }, { key: 'isActive', value: function isActive()
    {
      return this.active;
    }

    /**
       * 设置玩家的自定义属性
       * @param {Object} properties 自定义属性
       * @param {Object} [opts] 设置选项
       * @param {Object} [opts.expectedValues] 期望属性，用于 CAS 检测
       */ }, { key: 'setCustomProperties', value: function setCustomProperties(
    properties) {var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},_ref$expectedValues = _ref.expectedValues,expectedValues = _ref$expectedValues === undefined ? null : _ref$expectedValues;
      this._play._setPlayerCustomProperties(
      this._actorId,
      properties,
      expectedValues);

    }

    /**
       * @deprecated
       * 获取自定义属性
       * @return {Object}
       */ }, { key: 'getCustomProperties', value: function getCustomProperties()
    {
      return this.properties;
    }

    /**
       * 获取自定义属性
       * @return {Object}
       */ }, { key: '_setActive',




    // 设置活跃状态
    value: function _setActive(active) {
      this.active = active;
    } }, { key: '_mergeProperties', value: function _mergeProperties(

    changedProperties) {
      this.properties = _Object$assign(this.properties, changedProperties);
    } }, { key: 'userId', get: function get() {return this._userId;} /**
                                                                      * 房间玩家 ID
                                                                      * @type {number}
                                                                      * @readonly
                                                                      */ }, { key: 'actorId', get: function get() {return this._actorId;} }, { key: 'CustomProperties', get: function get() {return this.properties;} }], [{ key: '_newFromJSONObject', value: function _newFromJSONObject(play, playerJSONObject) {var player = new Player(play);player._initWithJSONObject(playerJSONObject);return player;} }]);return Player;}();

/**
                                                                                                                                  * 大厅房间数据类
                                                                                                                                  */var
LobbyRoom = function () {
  function LobbyRoom(lobbyRoomDTO) {_classCallCheck(this, LobbyRoom);
    this._roomName = lobbyRoomDTO.cid;
    this._maxPlayerCount = lobbyRoomDTO.maxMembers;
    this._expectedUserIds = lobbyRoomDTO.expectMembers;
    this._emptyRoomTtl = lobbyRoomDTO.emptyRoomTtl;
    this._playerTtl = lobbyRoomDTO.playerTtl;
    this._playerCount = lobbyRoomDTO.playerCount;
    if (lobbyRoomDTO.attr) {
      this._customRoomProperties = lobbyRoomDTO.attr;
    }
  }

  /**
     * 房间名称
     * @type {string}
     * @readonly
     */_createClass(LobbyRoom, [{ key: "roomName", get: function get()
    {
      return this._roomName;
    }

    /**
       * 房间最大玩家数
       * @type {number}
       * @readonly
       */ }, { key: "maxPlayerCount", get: function get()
    {
      return this._maxPlayerCount;
    }

    /**
       * 邀请好友 ID 数组
       * @type {Array.<string>}
       * @readonly
       */ }, { key: "expectedUserIds", get: function get()
    {
      return this._expectedUserIds;
    }

    /**
       * 房间置空后销毁时间（秒）
       * @type {number}
       * @readonly
       */ }, { key: "emptyRoomTtl", get: function get()
    {
      return this._emptyRoomTtl;
    }

    /**
       * 玩家离线后踢出房间时间（秒）
       * @type {number}
       * @readonly
       */ }, { key: "playerTtl", get: function get()
    {
      return this._playerTtl;
    }

    /**
       * 当前房间玩家数量
       * @type {number}
       * @readonly
       */ }, { key: "playerCount", get: function get()
    {
      return this._playerCount;
    }

    /**
       * 房间属性
       * @type {Object}
       * @readonly
       */ }, { key: "customRoomProperties", get: function get()
    {
      return this._customRoomProperties;
    } }]);return LobbyRoom;}();

var _logger;
var d = _debug('Play');

/**
                         * 日志级别
                         */
var LogLevel = {
  Debug: 'Debug',
  Warn: 'Warn',
  Error: 'Error' };


var logger = (_logger = {}, _defineProperty(_logger,
LogLevel.Debug, d), _defineProperty(_logger,
LogLevel.Warn, console.warn.bind(console)), _defineProperty(_logger,
LogLevel.Error, console.error.bind(console)), _logger);


function setLogger(newLogger) {
  _Object$assign(logger, newLogger);
}

/**
   * 调试输出
   * @param {String} log
   */
function debug(log) {
  var fullLog = '[DEBUG] ' + log;
  logger[LogLevel.Debug](fullLog);
}

/**
   * 警告输出
   * @param {String} log
   */
function warn(log) {
  var fullLog = '[WARN] ' + log;
  logger[LogLevel.Warn](fullLog);
}

/**
   * 错误输出
   * @param {String} log
   */
function error(log) {
  var fullLog = '[ERROR] ' + log;
  logger[LogLevel.Error](fullLog);
}

function handleErrorMsg(play, msg) {
  error('error: ' + _JSON$stringify(msg));
  play.emit(Event.ERROR, {
    code: msg.reasonCode,
    detail: msg.detail });

}

/**
 * 连接状态
 */
var PlayState = {
  /**
                   * 关闭
                   */
  CLOSED: 0,
  /**
              * 连接中
              */
  CONNECTING: 1,
  /**
                  * 大厅连接成功
                  */
  LOBBY_OPEN: 2,
  /**
                  * 房间连接成功
                  */
  GAME_OPEN: 3,
  /**
                 * 关闭中
                 */
  CLOSING: 4 };

// 连接建立
function handleSessionOpen(play, msg) {
  play._playState = PlayState.LOBBY_OPEN;
  play._sessionToken = msg.st;
  var player = new Player(play);
  player._userId = play.userId;
  play._player = player;
  if (play.autoJoinLobby) {
    play.joinLobby();
  }
  if (play._gameToLobby) {
    play.emit(Event.ROOM_LEFT);
    play._gameToLobby = false;
  } else {
    play.emit(Event.CONNECTED);
  }
}

// 加入大厅
function handleJoinedLobby(play, msg) {
  if (msg.reasonCode) {var
    reasonCode = msg.reasonCode,detail = msg.detail;
    error('join lobby failed: ' + reasonCode + ' - ' + detail);
  } else {
    play._inLobby = true;
    play.emit(Event.LOBBY_JOINED);
  }
}

// 离开大厅
function handleLeftLobby(play) {
  play._inLobby = false;
  play.emit(Event.LOBBY_LEFT);
}

// 房间列表更新
function handleRoomList(play, msg) {
  play._lobbyRoomList = [];
  for (var i = 0; i < msg.list.length; i += 1) {
    var lobbyRoomDTO = msg.list[i];
    play._lobbyRoomList[i] = new LobbyRoom(lobbyRoomDTO);
  }
  play.emit(Event.LOBBY_ROOM_LIST_UPDATED);
}

function handleGameServer(play, msg) {
  if (play._inLobby) {
    play._inLobby = false;
    play.emit(Event.LOBBY_LEFT);
  }
  play._gameServer = msg.secureAddr;
  if (msg.cid) {
    play._cachedRoomMsg.cid = msg.cid;
  }
  play._connectToGame();
}

// 创建房间
function handleCreateGameServer(play, msg) {
  if (msg.reasonCode) {
    play.emit(Event.ROOM_CREATE_FAILED, {
      code: msg.reasonCode,
      detail: msg.detail });

  } else {
    play._cachedRoomMsg.op = 'start';
    handleGameServer(play, msg);
  }
}

// 加入房间
/* eslint no-param-reassign: ["error", { "props": false }] */
function handleJoinGameServer(play, msg) {
  if (msg.reasonCode) {
    play.emit(Event.ROOM_JOIN_FAILED, {
      code: msg.reasonCode,
      detail: msg.detail });

  } else {
    play._cachedRoomMsg.op = 'add';
    handleGameServer(play, msg);
  }
}

// 大厅消息处理
function handleLobbyMsg(play, message) {
  var msg = JSON.parse(message.data);
  debug(play.userId + ' Lobby msg: ' + msg.op + ' \n<- ' + message.data);
  switch (msg.cmd) {
    case 'session':
      switch (msg.op) {
        case 'opened':
          handleSessionOpen(play, msg);
          break;
        default:
          error('no handler for lobby msg: ' + msg.op);
          break;}

      break;
    case 'lobby':
      switch (msg.op) {
        case 'added':
          handleJoinedLobby(play, msg);
          break;
        case 'room-list':
          handleRoomList(play, msg);
          break;
        case 'remove':
          handleLeftLobby(play);
          break;
        default:
          error('no handler for lobby msg: ' + msg.op);
          break;}

      break;
    case 'statistic':
      break;
    case 'conv':
      switch (msg.op) {
        case 'results':
          handleRoomList(play, msg);
          break;
        case 'started':
          handleCreateGameServer(play, msg);
          break;
        case 'added':
          handleJoinGameServer(play, msg);
          break;
        case 'random-added':
          handleJoinGameServer(play, msg);
          break;
        default:
          error('no handler for lobby msg: ' + msg.op);
          break;}

      break;
    case 'events':
      // TODO

      break;
    case 'error':
      handleErrorMsg(play, msg);
      break;
    default:
      if (msg.cmd) {
        error('no handler for lobby msg: ' + msg.cmd);
      }
      break;}

}

/**
                                                                                                                                                                                                                                                                                                  * 房间类
                                                                                                                                                                                                                                                                                                  */var
Room = function () {
  function Room(play) {_classCallCheck(this, Room);
    this._play = play;
  }

  /* eslint no-param-reassign: ["error", { "props": false }] */_createClass(Room, [{ key: 'getPlayer',























































































    /**
                                                                                                        * 根据 actorId 获取 Player 对象
                                                                                                        * @param {number} actorId 玩家在房间中的 Id
                                                                                                        * @return {Player}
                                                                                                        */value: function getPlayer(
    actorId) {
      if (!(typeof actorId === 'number')) {
        throw new TypeError(actorId + ' is not a number');
      }
      var player = this._players[actorId];
      if (player === null) {
        throw new TypeError('player with id:' + actorId + ' not found');
      }
      return player;
    }

    /**
       * 获取房间内的玩家列表
       * @return {Array.<Player>}
       * @readonly
       */ }, { key: 'setCustomProperties',




    /**
                                            * 设置房间的自定义属性
                                            * @param {Object} properties 自定义属性
                                            * @param {Object} [opts] 设置选项
                                            * @param {Object} [opts.expectedValues] 期望属性，用于 CAS 检测
                                            */value: function setCustomProperties(
    properties) {var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},_ref$expectedValues = _ref.expectedValues,expectedValues = _ref$expectedValues === undefined ? null : _ref$expectedValues;
      this._play._setRoomCustomProperties(properties, expectedValues);
    }

    /**
       * @deprecated
       * 获取自定义属性
       * @return {Object}
       */ }, { key: 'getCustomProperties', value: function getCustomProperties()
    {
      return this._properties;
    }

    /**
       * 获取自定义属性
       * @return {Object}
       */ }, { key: '_addPlayer', value: function _addPlayer(




    newPlayer) {
      if (!(newPlayer instanceof Player)) {
        throw new TypeError(newPlayer + ' is not a Player');
      }
      this._players[newPlayer.actorId] = newPlayer;
    } }, { key: '_removePlayer', value: function _removePlayer(

    actorId) {
      delete this._players[actorId];
    } }, { key: '_mergeProperties', value: function _mergeProperties(

    changedProperties) {
      this._properties = _Object$assign(this._properties, changedProperties);
    } }, { key: 'name', /**
                         * 房间名称
                         * @type {string}
                         * @readonly
                         */get: function get() {return this._name;} /**
                                                                     * 房间是否开启
                                                                     * @type {boolean}
                                                                     * @readonly
                                                                     */ }, { key: 'opened', get: function get() {return this._opened;} /**
                                                                                                                                        * 房间是否可见
                                                                                                                                        * @type {boolean}
                                                                                                                                        * @readonly
                                                                                                                                        */ }, { key: 'visible', get: function get() {return this._visible;} /**
                                                                                                                                                                                                             * 房间允许的最大玩家数量
                                                                                                                                                                                                             * @type {number}
                                                                                                                                                                                                             * @readonly
                                                                                                                                                                                                             */ }, { key: 'maxPlayerCount', get: function get() {return this._maxPlayerCount;} /**
                                                                                                                                                                                                                                                                                                * 获取房主
                                                                                                                                                                                                                                                                                                * @readonly
                                                                                                                                                                                                                                                                                                */ }, { key: 'master', get: function get() {return this.getPlayer(this.masterId);} /**
                                                                                                                                                                                                                                                                                                                                                                                    * 房间主机玩家 ID
                                                                                                                                                                                                                                                                                                                                                                                    * @type {number}
                                                                                                                                                                                                                                                                                                                                                                                    * @readonly
                                                                                                                                                                                                                                                                                                                                                                                    */ }, { key: 'masterId', get: function get() {return this._masterActorId;} /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                * 邀请的好友 ID 列表
                                                                                                                                                                                                                                                                                                                                                                                                                                                                * @type {Array.<string>}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                * @readonly
                                                                                                                                                                                                                                                                                                                                                                                                                                                                */ }, { key: 'expectedUserIds', get: function get() {return this._expectedUserIds;} }, { key: 'playerList', get: function get() {return _Object$values(this._players);} }, { key: 'CustomProperties', get: function get() {return this._properties;} }], [{ key: '_newFromJSONObject', value: function _newFromJSONObject(play, roomJSONObject) {var room = new Room(play);room._name = roomJSONObject.cid;room._opened = roomJSONObject.open;room._visible = roomJSONObject.visible;room._maxPlayerCount = roomJSONObject.maxMembers;room._masterActorId = roomJSONObject.masterActorId;room._expectedUserIds = roomJSONObject.expectMembers;room._players = {};for (var i = 0; i < roomJSONObject.members.length; i += 1) {var playerDTO = roomJSONObject.members[i];var player = Player._newFromJSONObject(play, playerDTO);if (player.userId === play.userId) {play._player = player;}room._players[player.actorId] = player;}if (roomJSONObject.attr) {room._properties = roomJSONObject.attr;} else {room._properties = {};}return room;} }]);return Room;}();

// 连接建立后创建 / 加入房间
function handleSessionOpen$1(play, msg) {
  if (msg.reasonCode) {
    play._playState = PlayState.LOBBY_OPEN;
    play._closeGameSocket(function () {
      play.emit(Event.ERROR, {
        code: msg.reasonCode,
        detail: msg.detail });

    });
  } else {
    // 根据缓存加入房间的规则
    play._cachedRoomMsg.i = play._getMsgId();
    play._sendGameMessage(play._cachedRoomMsg);
  }
}

function handleSessionClose(play) {
  // 收到 closed 协议后，客户端主动断开连接
  play._closeGameSocket();
}

// 创建房间
function handleCreatedRoom(play, msg) {
  if (msg.reasonCode) {
    play._playState = PlayState.LOBBY_OPEN;
    play._closeGameSocket(function () {
      play.emit(Event.ROOM_CREATE_FAILED, {
        code: msg.reasonCode,
        detail: msg.detail });

    });
  } else {
    play._closeLobbySocket(function () {
      play._playState = PlayState.GAME_OPEN;
      play._room = Room._newFromJSONObject(play, msg);
      play.emit(Event.ROOM_CREATED);
      play.emit(Event.ROOM_JOINED);
    });
  }
}

// 加入房间
function handleJoinedRoom(play, msg) {
  if (msg.reasonCode) {
    play._playState = PlayState.LOBBY_OPEN;
    play._closeGameSocket(function () {
      play.emit(Event.ROOM_JOIN_FAILED, {
        code: msg.reasonCode,
        detail: msg.detail });

    });
  } else {
    play._closeLobbySocket(function () {
      play._playState = PlayState.GAME_OPEN;
      play._room = Room._newFromJSONObject(play, msg);
      play.emit(Event.ROOM_JOINED);
    });
  }
}

// 有新玩家加入房间
function handleNewPlayerJoinedRoom(play, msg) {
  var newPlayer = Player._newFromJSONObject(play, msg.member);
  play._room._addPlayer(newPlayer);
  play.emit(Event.PLAYER_ROOM_JOINED, {
    newPlayer: newPlayer });

}

// 有玩家离开房间
function handlePlayerLeftRoom(play, msg) {
  var actorId = msg.initByActor;
  var leftPlayer = play._room.getPlayer(actorId);
  play._room._removePlayer(actorId);
  play.emit(Event.PLAYER_ROOM_LEFT, {
    leftPlayer: leftPlayer });

}

// 主机切换应答
function handleMasterUpdated(msg) {
  if (msg.reasonCode) {
    error('set master error: ' + msg.reasonCode + ', ' + msg.detail);
  }
}

// 主机切换
function handleMasterChanged(play, msg) {
  if (play === null) {
    debug('play is null');
  } else if (play._room === null) {
    debug('play _room is null');
    debug(play.userId);
  }
  play._room._masterActorId = msg.masterActorId;
  var newMaster = play._room.getPlayer(msg.masterActorId);
  play.emit(Event.MASTER_SWITCHED, {
    newMaster: newMaster });

}

// 房间开启 / 关闭
function handleRoomOpenedChanged(play, msg) {
  var opened = msg.toggle;
  play._room._opened = opened;
  play.emit(Event.ROOM_OPEN_CHANGED, {
    opened: opened });

}

// 房间是否可见
function handleRoomVisibleChanged(play, msg) {
  var visible = msg.toggle;
  play._room._visible = visible;
  play.emit(Event.ROOM_VISIBLE_CHANGED, {
    visible: visible });

}

// 房间属性变更应答
function handleRoomCustomPropertiesChangedResponse(msg) {
  if (msg.reasonCode) {
    error('set room properties error: ' + msg.reasonCode + ', ' + msg.detail);
  }
}

// 房间属性变更
function handleRoomCustomPropertiesChanged(play, msg) {
  var changedProps = msg.attr;
  play._room._mergeProperties(changedProps);
  play.emit(Event.ROOM_CUSTOM_PROPERTIES_CHANGED, {
    changedProps: changedProps });

}

// 玩家属性变更
function handlePlayerCustomPropertiesChanged(play, msg) {
  var player = play._room.getPlayer(msg.actorId);
  player._mergeProperties(msg.attr);
  play.emit(Event.PLAYER_CUSTOM_PROPERTIES_CHANGED, {
    player: player,
    changedProps: msg.attr });

}

// 玩家下线
function handlePlayerOffline(play, msg) {
  var player = play._room.getPlayer(msg.initByActor);
  player._setActive(false);
  play.emit(Event.PLAYER_ACTIVITY_CHANGED, {
    player: player });

}

// 玩家上线
function handlePlayerOnline(play, msg) {
  var player = play._room.getPlayer(msg.member.actorId);
  player._initWithJSONObject(msg.member);
  player._setActive(true);
  play.emit(Event.PLAYER_ACTIVITY_CHANGED, {
    player: player });

}

// 离开房间
/* eslint no-param-reassign: ["error", { "props": false }] */
function handleLeaveRoom(play) {
  // 清理工作
  play._room = null;
  play._player = null;
  // 离开房间时就主动断开连接
  play._closeGameSocket(function () {
    play._connectToMaster(true);
  });
}

// 自定义事件
function handleEvent(play, msg) {
  play.emit(Event.CUSTOM_EVENT, {
    eventId: msg.eventId,
    eventData: msg.msg,
    senderId: msg.fromActorId });

}

function handleGameMsg(play, message) {
  var msg = JSON.parse(message.data);
  debug(play.userId + ' Game  msg: ' + msg.op + ' \n<- ' + message.data);
  switch (msg.cmd) {
    case 'session':
      switch (msg.op) {
        case 'opened':
          handleSessionOpen$1(play, msg);
          break;
        case 'closed':
          handleSessionClose(play);
          break;
        default:
          error('no handler for op: ' + msg.op);
          break;}

      break;
    case 'conv':
      switch (msg.op) {
        case 'started':
          handleCreatedRoom(play, msg);
          break;
        case 'added':
          handleJoinedRoom(play, msg);
          break;
        case 'members-joined':
          handleNewPlayerJoinedRoom(play, msg);
          break;
        case 'members-left':
          handlePlayerLeftRoom(play, msg);
          break;
        case 'master-client-updated':
          handleMasterUpdated(msg);
          break;
        case 'master-client-changed':
          handleMasterChanged(play, msg);
          break;
        case 'opened':
          break;
        case 'opened-notify':
          handleRoomOpenedChanged(play, msg);
          break;
        case 'visible':
          break;
        case 'visible-notify':
          handleRoomVisibleChanged(play, msg);
          break;
        case 'updated':
          handleRoomCustomPropertiesChangedResponse(msg);
          break;
        case 'updated-notify':
          handleRoomCustomPropertiesChanged(play, msg);
          break;
        case 'player-prop-updated':
          break;
        case 'player-props':
          handlePlayerCustomPropertiesChanged(play, msg);
          break;
        case 'members-offline':
          handlePlayerOffline(play, msg);
          break;
        case 'members-online':
          handlePlayerOnline(play, msg);
          break;
        case 'removed':
          handleLeaveRoom(play);
          break;
        default:
          error('no handler for game msg: ' + msg.op);
          break;}

      break;
    case 'direct':
      handleEvent(play, msg);
      break;
    case 'ack':
      // ignore
      break;
    case 'events':
      // TODO

      break;
    case 'error':
      handleErrorMsg(play, msg);
      break;
    default:
      if (msg.cmd) {
        error('no handler for cmd: ' + message.data);
      }
      break;}

}

var version = "0.13.23";

// SDK 版本号
var NorthCNServerURL =
'https://game-router-cn-n1.leancloud.cn/v1/router';
var EastCNServerURL =
'https://game-router-cn-e1.leancloud.cn/v1/router';
var USServerURL = 'https://game-router-us-w1.leancloud.cn/v1/router';

var adapters = {
  WebSocket: WebSocket };


/**
                           * 设置适配器
                           * @param {Object} newAdapters
                           * @param {Function} newAdapters.WebSocketAdapter WebSocket 适配器，Cocos Creator 打包 android 平台时需要传入 CA 证书
                           */
function setAdapters(newAdapters) {
  _Object$assign(adapters, newAdapters);
}

var isWeapp =
// eslint-disable-next-line no-undef
(typeof wx === 'undefined' ? 'undefined' : _typeof(wx)) === 'object' && typeof wx.connectSocket === 'function';

var MAX_PLAYER_COUNT = 10;
var LOBBY_KEEPALIVE_DURATION = 120000;
var GAME_KEEPALIVE_DURATION = 10000;
var MAX_NO_PONG_TIMES = 3;

function convertRoomOptions(roomOptions) {
  var options = {};
  if (!roomOptions.opened) options.open = roomOptions.opened;
  if (!roomOptions.visible) options.visible = roomOptions.visible;
  if (roomOptions.emptyRoomTtl > 0)
  options.emptyRoomTtl = roomOptions.emptyRoomTtl;
  if (roomOptions.playerTtl > 0) options.playerTtl = roomOptions.playerTtl;
  if (
  roomOptions.maxPlayerCount > 0 &&
  roomOptions.maxPlayerCount < MAX_PLAYER_COUNT)

  options.maxMembers = roomOptions.maxPlayerCount;
  if (roomOptions.customRoomProperties)
  options.attr = roomOptions.customRoomProperties;
  if (roomOptions.customRoomPropertyKeysForLobby)
  options.lobbyAttrKeys = roomOptions.customRoomPropertyKeysForLobby;
  if (roomOptions.flag) options.flag = roomOptions.flag;
  return options;
}

function _closeSocket(websocket, onClose) {
  var ws = websocket;
  if (ws) {
    ws.onopen = null;
    ws.onconnect = null;
    ws.onmessage = null;
    ws.onclose = onClose;
    try {
      ws.close();
    } catch (e) {
      debug('close socket exception: ' + e);
    }
  } else if (onClose) {
    onClose();
  }
}

/**
   * Play 客户端类
   */var
Play = function (_EventEmitter) {_inherits(Play, _EventEmitter);function Play() {_classCallCheck(this, Play);return _possibleConstructorReturn(this, (Play.__proto__ || _Object$getPrototypeOf(Play)).apply(this, arguments));}_createClass(Play, [{ key: 'init',
    /**
                                                                                                                                                                                                                                                                   * 初始化客户端
                                                                                                                                                                                                                                                                   * @param {Object} opts
                                                                                                                                                                                                                                                                   * @param {string} opts.appId APP ID
                                                                                                                                                                                                                                                                   * @param {string} opts.appKey APP KEY
                                                                                                                                                                                                                                                                   * @param {number} opts.region 节点地区
                                                                                                                                                                                                                                                                   */value: function init(
    opts) {
      if (!(typeof opts.appId === 'string')) {
        throw new TypeError(opts.appId + ' is not a string');
      }
      if (!(typeof opts.appKey === 'string')) {
        throw new TypeError(opts.appKey + ' is not a string');
      }
      if (!(typeof opts.region === 'number')) {
        throw new TypeError(opts.region + ' is not a number');
      }
      if (opts.feature !== undefined && !(typeof opts.feature === 'string')) {
        throw new TypeError(opts.feature + ' is not a string');
      }
      this._appId = opts.appId;
      this._appKey = opts.appKey;
      this._region = opts.region;
      this._feature = opts.feature;
      /**
                                     * 玩家 ID
                                     * @type {string}
                                     */
      this.userId = null;
      this.reset();
    }

    /**
       * 建立连接
       * @param {Object} [opts] 连接选项
       * @param {string} [opts.gameVersion] 游戏版本号，不同的游戏版本号将路由到不同的服务端，默认值为 0.0.1
       */ }, { key: 'connect', value: function connect()
    {var _this2 = this;var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},_ref$gameVersion = _ref.gameVersion,gameVersion = _ref$gameVersion === undefined ? '0.0.1' : _ref$gameVersion;
      // 判断是否有 userId
      if (this.userId === null) {
        throw new Error('userId is null');
      }
      // 判断是否是「断开」状态
      if (this._playState !== PlayState.CLOSED) {
        throw new Error('play state error: ' + this._playState);
      }
      // 判断是否已经在等待连接
      if (this._connectTimer) {
        warn('waiting for connect');
        return;
      }

      // 判断连接时间
      var now = new Date().getTime();
      if (now < this._nextConnectTimestamp) {
        var waitTime = this._nextConnectTimestamp - now;
        debug('wait time: ' + waitTime);
        this._connectTimer = setTimeout(function () {
          debug('connect time out');
          _this2._connect(gameVersion);
          clearTimeout(_this2._connectTimer);
          _this2._connectTimer = null;
        }, waitTime);
      } else {
        this._connect(gameVersion);
      }
    } }, { key: '_connect', value: function _connect(

    gameVersion) {var _this3 = this;
      if (gameVersion && !(typeof gameVersion === 'string')) {
        throw new TypeError(gameVersion + ' is not a string');
      }
      this._gameVersion = gameVersion;
      var masterURL = EastCNServerURL;
      if (this._region === Region.NorthChina) {
        masterURL = NorthCNServerURL;
      } else if (this._region === Region.EastChina) {
        masterURL = EastCNServerURL;
      } else if (this._region === Region.NorthAmerica) {
        masterURL = USServerURL;
      }

      this._playState = PlayState.CONNECTING;
      var query = { appId: this._appId, sdkVersion: version };
      // 使用设置覆盖 SDK 判断的 feature
      if (this._feature) {
        query.feature = this._feature;
      } else if (isWeapp) {
        query.feature = 'wechat';
      }
      this._httpReq = request.
      get(masterURL).
      query(query).
      end(function (err, response) {
        if (err) {
          error(err);
          // 连接失败，则增加下次连接时间间隔
          _this3._connectFailedCount += 1;
          _this3._nextConnectTimestamp =
          Date.now() + Math.pow(2, _this3._connectFailedCount) * 1000;
          _this3.emit(Event.CONNECT_FAILED, {
            code: -1,
            detail: 'Game router connect failed' });

        } else {
          var body = JSON.parse(response.text);
          debug(response.text);
          // 重置下次允许的连接时间
          _this3._connectFailedCount = 0;
          _this3._nextConnectTimestamp = 0;
          clearTimeout(_this3._connectTimer);
          _this3._connectTimer = null;
          // 主大厅服务器
          _this3._primaryServer = body.server;
          // 备用大厅服务器
          _this3._secondaryServer = body.secondary;
          // 默认服务器是 master server
          _this3._masterServer = _this3._primaryServer;
          // ttl
          _this3._serverValidTimeStamp = Date.now() + body.ttl * 1000;
          _this3._connectToMaster();
        }
      });
    }

    /**
       * 重新连接
       */ }, { key: 'reconnect', value: function reconnect()
    {
      var now = Date.now();
      if (now > this._serverValidTimeStamp) {
        // 超出 ttl 后将重新请求 router 连接
        this.connect(this._gameVersion);
      } else {
        this._connectToMaster();
      }
    }

    /**
       * 重新连接并自动加入房间
       */ }, { key: 'reconnectAndRejoin', value: function reconnectAndRejoin()
    {
      if (this._cachedRoomMsg === null) {
        throw new Error('no cache room info');
      }
      if (this._cachedRoomMsg.cid === undefined) {
        throw new Error('not cache room name');
      }
      this._cachedRoomMsg = {
        cmd: 'conv',
        op: 'add',
        i: this._getMsgId(),
        cid: this._cachedRoomMsg.cid,
        rejoin: true };

      this._connectToGame();
    }

    /**
       * 断开连接
       */ }, { key: 'disconnect', value: function disconnect()
    {var _this4 = this;
      if (
      this._playState !== PlayState.LOBBY_OPEN &&
      this._playState !== PlayState.GAME_OPEN)
      {
        throw new Error('error play state: ' + this._playState);
      }
      this._playState = PlayState.CLOSING;
      this._stopPing();
      this._stopPong();
      this._closeLobbySocket(function () {
        debug('on close lobby socket');
        _this4._closeGameSocket(function () {
          debug('on close game socket');
          _this4._playState = PlayState.CLOSED;
          _this4.emit(Event.DISCONNECTED);
          debug(_this4.userId + ' disconnect.');
        });
      });
    }

    /**
       * 重置
       */ }, { key: 'reset', value: function reset()
    {
      this._room = null;
      this._player = null;
      this._cachedRoomMsg = null;
      this._playState = PlayState.CLOSED;
      this._masterServer = null;
      this._gameServer = null;
      this._msgId = 0;
      this._inLobby = false;
      this._lobbyRoomList = null;
      this._connectFailedCount = 0;
      this._nextConnectTimestamp = 0;
      this._gameToLobby = false;
      this._stopConnectTimer();
      this._cancelHttp();
      this._stopPing();
      this._stopPong();
      this._closeLobbySocket();
      this._closeGameSocket();
    }

    /**
       * 加入大厅，只有在 autoJoinLobby = false 时才需要调用
       */ }, { key: 'joinLobby', value: function joinLobby()
    {
      if (this._playState !== PlayState.LOBBY_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      var msg = {
        cmd: 'lobby',
        op: 'add',
        i: this._getMsgId() };

      this._sendLobbyMessage(msg);
    }

    /**
       * 离开大厅
       */ }, { key: 'leaveLobby', value: function leaveLobby()
    {
      if (this._playState !== PlayState.LOBBY_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      var msg = {
        cmd: 'lobby',
        op: 'remove',
        i: this._getMsgId() };

      this._sendLobbyMessage(msg);
    }

    /**
       * 创建房间
       * @param {Object} [opts] 创建房间选项
       * @param {string} [opts.roomName] 房间名称，在整个游戏中唯一，默认值为 null，则由服务端分配一个唯一 Id
       * @param {Object} [opts.roomOptions] 创建房间选项，默认值为 null
       * @param {Boolean} [opts.roomOptions.opened] 房间是否打开
       * @param {Boolean} [opts.roomOptions.visible] 房间是否可见，只有「可见」的房间会出现在房间列表里
       * @param {Number} [opts.roomOptions.emptyRoomTtl] 房间为空后，延迟销毁的时间
       * @param {Number} [opts.roomOptions.playerTtl] 玩家掉线后，延迟销毁的时间
       * @param {Number} [opts.roomOptions.maxPlayerCount] 最大玩家数量
       * @param {Object} [opts.roomOptions.customRoomProperties] 自定义房间属性
       * @param {Array.<string>} [opts.roomOptions.customRoomPropertyKeysForLobby] 在大厅中可获得的房间属性「键」数组
       * @param {CreateRoomFlag} [opts.roomOptions.flag] 创建房间标记，可多选
       * @param {Array.<string>} [opts.expectedUserIds] 邀请好友 ID 数组，默认值为 null
       */ }, { key: 'createRoom', value: function createRoom()




    {var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},_ref2$roomName = _ref2.roomName,roomName = _ref2$roomName === undefined ? null : _ref2$roomName,_ref2$roomOptions = _ref2.roomOptions,roomOptions = _ref2$roomOptions === undefined ? null : _ref2$roomOptions,_ref2$expectedUserIds = _ref2.expectedUserIds,expectedUserIds = _ref2$expectedUserIds === undefined ? null : _ref2$expectedUserIds;
      if (roomName !== null && !(typeof roomName === 'string')) {
        throw new TypeError(roomName + ' is not a string');
      }
      if (roomOptions !== null && !(roomOptions instanceof Object)) {
        throw new TypeError(roomOptions + ' is not a Object');
      }
      if (expectedUserIds !== null && !Array.isArray(expectedUserIds)) {
        throw new TypeError(expectedUserIds + ' is not an Array with string');
      }
      if (this._playState !== PlayState.LOBBY_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      // 缓存 GameServer 创建房间的消息体
      this._cachedRoomMsg = {
        cmd: 'conv',
        op: 'start',
        i: this._getMsgId() };

      if (roomName) {
        this._cachedRoomMsg.cid = roomName;
      }
      // 拷贝房间属性（包括 系统属性和玩家定义属性）
      if (roomOptions) {
        var opts = convertRoomOptions(roomOptions);
        this._cachedRoomMsg = _Object$assign(this._cachedRoomMsg, opts);
      }
      if (expectedUserIds) {
        this._cachedRoomMsg.expectMembers = expectedUserIds;
      }
      // Router 创建房间的消息体
      var msg = this._cachedRoomMsg;
      this._sendLobbyMessage(msg);
    }

    /**
       * 加入房间
       * @param {string} roomName 房间名称
       * @param {*} [expectedUserIds] 邀请好友 ID 数组，默认值为 null
       */ }, { key: 'joinRoom', value: function joinRoom(
    roomName) {var _ref3 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},_ref3$expectedUserIds = _ref3.expectedUserIds,expectedUserIds = _ref3$expectedUserIds === undefined ? null : _ref3$expectedUserIds;
      if (!(typeof roomName === 'string')) {
        throw new TypeError(roomName + ' is not a string');
      }
      if (expectedUserIds !== null && !Array.isArray(expectedUserIds)) {
        throw new TypeError(expectedUserIds + ' is not an array with string');
      }
      if (this._playState !== PlayState.LOBBY_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      // 加入房间的消息体
      this._cachedRoomMsg = {
        cmd: 'conv',
        op: 'add',
        i: this._getMsgId(),
        cid: roomName };

      if (expectedUserIds) {
        this._cachedRoomMsg.expectMembers = expectedUserIds;
      }
      var msg = this._cachedRoomMsg;
      this._sendLobbyMessage(msg);
    }

    /**
       * 重新加入房间
       * @param {string} roomName 房间名称
       */ }, { key: 'rejoinRoom', value: function rejoinRoom(
    roomName) {
      if (!(typeof roomName === 'string')) {
        throw new TypeError(roomName + ' is not a string');
      }
      if (this._playState !== PlayState.LOBBY_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      this._cachedRoomMsg = {
        cmd: 'conv',
        op: 'add',
        i: this._getMsgId(),
        cid: roomName,
        rejoin: true };

      var msg = this._cachedRoomMsg;
      this._sendLobbyMessage(msg);
    }

    /**
       * 随机加入或创建房间
       * @param {string} roomName 房间名称
       * @param {Object} [opts] 创建房间选项
       * @param {Object} [opts.roomOptions] 创建房间选项，默认值为 null
       * @param {Boolean} [opts.roomOptions.opened] 房间是否打开
       * @param {Boolean} [opts.roomOptions.visible] 房间是否可见，只有「可见」的房间会出现在房间列表里
       * @param {Number} [opts.roomOptions.emptyRoomTtl] 房间为空后，延迟销毁的时间
       * @param {Number} [opts.roomOptions.playerTtl] 玩家掉线后，延迟销毁的时间
       * @param {Number} [opts.roomOptions.maxPlayerCount] 最大玩家数量
       * @param {Object} [opts.roomOptions.customRoomProperties] 自定义房间属性
       * @param {Array.<string>} [opts.roomOptions.customRoomPropertyKeysForLobby] 在大厅中可获得的房间属性「键」数组
       * @param {CreateRoomFlag} [opts.roomOptions.flag] 创建房间标记，可多选
       * @param {Array.<string>} [opts.expectedUserIds] 邀请好友 ID 数组，默认值为 null
       */ }, { key: 'joinOrCreateRoom', value: function joinOrCreateRoom(

    roomName)

    {var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},_ref4$roomOptions = _ref4.roomOptions,roomOptions = _ref4$roomOptions === undefined ? null : _ref4$roomOptions,_ref4$expectedUserIds = _ref4.expectedUserIds,expectedUserIds = _ref4$expectedUserIds === undefined ? null : _ref4$expectedUserIds;
      if (!(typeof roomName === 'string')) {
        throw new TypeError(roomName + ' is not a string');
      }
      if (this._playState !== PlayState.LOBBY_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      if (roomOptions !== null && !(roomOptions instanceof Object)) {
        throw new TypeError(roomOptions + ' is not a Object');
      }
      if (expectedUserIds !== null && !Array.isArray(expectedUserIds)) {
        throw new TypeError(expectedUserIds + ' is not an array with string');
      }
      this._cachedRoomMsg = {
        cmd: 'conv',
        op: 'add',
        i: this._getMsgId(),
        cid: roomName };

      // 拷贝房间参数
      if (roomOptions != null) {
        var opts = convertRoomOptions(roomOptions);
        this._cachedRoomMsg = _Object$assign(this._cachedRoomMsg, opts);
      }
      if (expectedUserIds) {
        this._cachedRoomMsg.expectMembers = expectedUserIds;
      }
      var msg = {
        cmd: 'conv',
        op: 'add',
        i: this._getMsgId(),
        cid: roomName,
        createOnNotFound: true };

      if (expectedUserIds) {
        msg.expectMembers = expectedUserIds;
      }
      this._sendLobbyMessage(msg);
    }

    /**
       * 随机加入房间
       * @param {Object} [opts] 随机加入房间选项
       * @param {Object} [opts.matchProperties] 匹配属性，默认值为 null
       * @param {Array.<string>} [opts.expectedUserIds] 邀请好友 ID 数组，默认值为 null
       */ }, { key: 'joinRandomRoom', value: function joinRandomRoom()
    {var _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},_ref5$matchProperties = _ref5.matchProperties,matchProperties = _ref5$matchProperties === undefined ? null : _ref5$matchProperties,_ref5$expectedUserIds = _ref5.expectedUserIds,expectedUserIds = _ref5$expectedUserIds === undefined ? null : _ref5$expectedUserIds;
      if (matchProperties !== null && !((typeof matchProperties === 'undefined' ? 'undefined' : _typeof(matchProperties)) === 'object')) {
        throw new TypeError(matchProperties + ' is not an object');
      }
      if (expectedUserIds !== null && !Array.isArray(expectedUserIds)) {
        throw new TypeError(expectedUserIds + ' is not an array with string');
      }
      if (this._playState !== PlayState.LOBBY_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      this._cachedRoomMsg = {
        cmd: 'conv',
        op: 'add',
        i: this._getMsgId() };

      if (matchProperties) {
        this._cachedRoomMsg.expectAttr = matchProperties;
      }
      if (expectedUserIds) {
        this._cachedRoomMsg.expectMembers = expectedUserIds;
      }

      var msg = {
        cmd: 'conv',
        op: 'add-random' };

      if (matchProperties) {
        msg.expectAttr = matchProperties;
      }
      if (expectedUserIds) {
        msg.expectMembers = expectedUserIds;
      }
      this._sendLobbyMessage(msg);
    }

    /**
       * 设置房间开启 / 关闭
       * @param {Boolean} opened 是否开启
       */ }, { key: 'setRoomOpened', value: function setRoomOpened(
    opened) {
      if (!(typeof opened === 'boolean')) {
        throw new TypeError(opened + ' is not a boolean value');
      }
      if (this._room === null) {
        throw new Error('room is null');
      }
      if (this._playState !== PlayState.GAME_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      var msg = {
        cmd: 'conv',
        op: 'open',
        i: this._getMsgId(),
        toggle: opened };

      this._sendGameMessage(msg);
    }

    /**
       * 设置房间可见 / 不可见
       * @param {Boolean} visible 是否可见
       */ }, { key: 'setRoomVisible', value: function setRoomVisible(
    visible) {
      if (!(typeof visible === 'boolean')) {
        throw new TypeError(visible + ' is not a boolean value');
      }
      if (this._room === null) {
        throw new Error('room is null');
      }
      if (this._playState !== PlayState.GAME_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      var msg = {
        cmd: 'conv',
        op: 'visible',
        i: this._getMsgId(),
        toggle: visible };

      this._sendGameMessage(msg);
    }

    /**
       * 设置房主
       * @param {number} newMasterId 新房主 ID
       */ }, { key: 'setMaster', value: function setMaster(
    newMasterId) {
      if (!(typeof newMasterId === 'number')) {
        throw new TypeError(newMasterId + ' is not a number');
      }
      if (this._room === null) {
        throw new Error('room is null');
      }
      if (this._playState !== PlayState.GAME_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      var msg = {
        cmd: 'conv',
        op: 'update-master-client',
        i: this._getMsgId(),
        masterActorId: newMasterId };

      this._sendGameMessage(msg);
    }

    /**
       * 发送自定义消息
       * @param {number|string} eventId 事件 ID
       * @param {Object} eventData 事件参数
       * @param {Object} options 发送事件选项
       * @param {ReceiverGroup} options.receiverGroup 接收组
       * @param {Array.<number>} options.targetActorIds 接收者 Id。如果设置，将会覆盖 receiverGroup
       */ }, { key: 'sendEvent', value: function sendEvent(
    eventId, eventData, options) {
      if (!(typeof eventId === 'string') && !(typeof eventId === 'number')) {
        throw new TypeError(eventId + ' is not a string or number');
      }
      if (!((typeof eventData === 'undefined' ? 'undefined' : _typeof(eventData)) === 'object')) {
        throw new TypeError(eventData + ' is not an object');
      }
      if (!(options instanceof Object)) {
        throw new TypeError(options + ' is not a Object');
      }
      if (
      options.receiverGroup === undefined &&
      options.targetActorIds === undefined)
      {
        throw new TypeError('receiverGroup and targetActorIds are null');
      }
      if (this._room === null) {
        throw new Error('room is null');
      }
      if (this._player === null) {
        throw new Error('player is null');
      }
      if (this._playState !== PlayState.GAME_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      var msg = {
        cmd: 'direct',
        i: this._getMsgId(),
        eventId: eventId,
        msg: eventData,
        receiverGroup: options.receiverGroup,
        toActorIds: options.targetActorIds };

      this._sendGameMessage(msg);
    }

    /**
       * 离开房间
       */ }, { key: 'leaveRoom', value: function leaveRoom()
    {
      if (this._playState !== PlayState.GAME_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      var msg = {
        cmd: 'conv',
        op: 'remove',
        i: this._getMsgId(),
        cid: this.room.name };

      this._sendGameMessage(msg);
    }

    /**
       * 获取当前所在房间
       * @return {Room}
       * @readonly
       */ }, { key: '_setRoomCustomProperties',






















    // 设置房间属性
    value: function _setRoomCustomProperties(properties, expectedValues) {
      if (!((typeof properties === 'undefined' ? 'undefined' : _typeof(properties)) === 'object')) {
        throw new TypeError(properties + ' is not an object');
      }
      if (expectedValues && !((typeof expectedValues === 'undefined' ? 'undefined' : _typeof(expectedValues)) === 'object')) {
        throw new TypeError(expectedValues + ' is not an object');
      }
      if (this._playState !== PlayState.GAME_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      var msg = {
        cmd: 'conv',
        op: 'update',
        i: this._getMsgId(),
        attr: properties };

      if (expectedValues) {
        msg.expectAttr = expectedValues;
      }
      this._sendGameMessage(msg);
    }

    // 设置玩家属性
  }, { key: '_setPlayerCustomProperties', value: function _setPlayerCustomProperties(actorId, properties, expectedValues) {
      if (!(typeof actorId === 'number')) {
        throw new TypeError(actorId + ' is not a number');
      }
      if (!((typeof properties === 'undefined' ? 'undefined' : _typeof(properties)) === 'object')) {
        throw new TypeError(properties + ' is not an object');
      }
      if (expectedValues && !((typeof expectedValues === 'undefined' ? 'undefined' : _typeof(expectedValues)) === 'object')) {
        throw new TypeError(expectedValues + ' is not an object');
      }
      if (this._playState !== PlayState.GAME_OPEN) {
        throw new Error('error play state: ' + this._playState);
      }
      var msg = {
        cmd: 'conv',
        op: 'update-player-prop',
        i: this._getMsgId(),
        targetActorId: actorId,
        attr: properties };

      if (expectedValues) {
        msg.expectAttr = expectedValues;
      }
      this._sendGameMessage(msg);
    }

    // 开始大厅会话
  }, { key: '_lobbySessionOpen', value: function _lobbySessionOpen() {
      var msg = {
        cmd: 'session',
        op: 'open',
        i: this._getMsgId(),
        appId: this._appId,
        peerId: this.userId,
        sdkVersion: version,
        gameVersion: this._gameVersion };

      this._sendLobbyMessage(msg);
    }

    // 开始房间会话
  }, { key: '_gameSessionOpen', value: function _gameSessionOpen() {
      var msg = {
        cmd: 'session',
        op: 'open',
        i: this._getMsgId(),
        appId: this._appId,
        peerId: this.userId,
        sdkVersion: version,
        gameVersion: this._gameVersion };

      this._sendGameMessage(msg);
    }

    // 发送大厅消息
  }, { key: '_sendLobbyMessage', value: function _sendLobbyMessage(msg) {
      this._send(this._lobbyWS, msg, 'Lobby', LOBBY_KEEPALIVE_DURATION);
    }

    // 发送房间消息
  }, { key: '_sendGameMessage', value: function _sendGameMessage(msg) {
      this._send(this._gameWS, msg, 'Game ', GAME_KEEPALIVE_DURATION);
    }

    // 发送消息
  }, { key: '_send', value: function _send(ws, msg, flag, duration) {var _this5 = this;
      if (!((typeof msg === 'undefined' ? 'undefined' : _typeof(msg)) === 'object')) {
        throw new TypeError(msg + ' is not an object');
      }
      var msgData = _JSON$stringify(msg);
      debug(this.userId + ' ' + flag + ' msg: ' + msg.op + ' \n-> ' + msgData);var
      WebSocket$$1 = adapters.WebSocket;
      if (ws.readyState === WebSocket$$1.OPEN) {
        ws.send(msgData);
        // 心跳包
        this._stopPing();
        this._ping = setTimeout(function () {
          debug('ping time out');
          var ping = {};
          _this5._send(ws, ping, flag, duration);
        }, duration);
      } else {
        this._stopPing();
        this._stopPong();
      }
    }

    // 连接至大厅服务器
  }, { key: '_connectToMaster', value: function _connectToMaster() {var _this6 = this;var gameToLobby = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
      this._playState = PlayState.CONNECTING;
      this._gameToLobby = gameToLobby;var
      WebSocket$$1 = adapters.WebSocket;
      this._lobbyWS = new WebSocket$$1(this._masterServer);
      this._lobbyWS.onopen = function () {
        debug('Lobby websocket opened');
        _this6._lobbySessionOpen();
      };
      this._lobbyWS.onmessage = function (msg) {
        _this6._stopPong();
        _this6._startPongListener(_this6._lobbyWS, LOBBY_KEEPALIVE_DURATION);
        handleLobbyMsg(_this6, msg);
      };
      this._lobbyWS.onclose = function () {
        debug('Lobby websocket closed');
        if (_this6._playState === PlayState.CONNECTING) {
          // 连接失败
          if (_this6._masterServer === _this6._secondaryServer) {
            _this6.emit(Event.CONNECT_FAILED, {
              code: -2,
              detail: 'Lobby socket connect failed' });

          } else {
            // 内部重连
            _this6._masterServer = _this6._secondaryServer;
            _this6._connectToMaster();
          }
        } else {
          // 断开连接
          _this6._playState = PlayState.CLOSED;
          _this6.emit(Event.DISCONNECTED);
        }
        _this6._stopPing();
        _this6._stopPong();
      };
      this._lobbyWS.onerror = function (err) {
        error(err);
      };
    }

    // 连接至游戏服务器
  }, { key: '_connectToGame', value: function _connectToGame() {var _this7 = this;
      this._playState = PlayState.CONNECTING;var
      WebSocket$$1 = adapters.WebSocket;
      this._gameWS = new WebSocket$$1(this._gameServer);
      this._gameWS.onopen = function () {
        debug('Game websocket opened');
        _this7._gameSessionOpen();
      };
      this._gameWS.onmessage = function (msg) {
        _this7._stopPong();
        _this7._startPongListener(_this7._gameWS, GAME_KEEPALIVE_DURATION);
        handleGameMsg(_this7, msg);
      };
      this._gameWS.onclose = function () {
        debug('Game websocket closed');
        if (_this7._playState === PlayState.CONNECTING) {
          // 连接失败
          _this7.emit(Event.CONNECT_FAILED, {
            code: -2,
            detail: 'Game socket connect failed' });

        } else {
          // 断开连接
          _this7._playState = PlayState.CLOSED;
          _this7.emit(Event.DISCONNECTED);
        }
        _this7._stopPing();
        _this7._stopPong();
      };
      this._gameWS.onerror = function (err) {
        error(err);
      };
    } }, { key: '_getMsgId', value: function _getMsgId()

    {
      this._msgId += 1;
      return this._msgId;
    } }, { key: '_stopConnectTimer', value: function _stopConnectTimer()

    {
      if (this._connectTimer) {
        clearTimeout(this._connectTimer);
        this._connectTimer = null;
      }
    } }, { key: '_stopPing', value: function _stopPing()

    {
      if (this._ping) {
        clearTimeout(this._ping);
        this._ping = null;
      }
    } }, { key: '_stopPong', value: function _stopPong()

    {
      if (this._pong) {
        clearTimeout(this._pong);
        this._pong = null;
      }
    } }, { key: '_startPongListener', value: function _startPongListener(

    ws, duration) {
      this._pong = setTimeout(function () {
        ws.close();
      }, duration * MAX_NO_PONG_TIMES);
    } }, { key: '_cancelHttp', value: function _cancelHttp()

    {
      if (this._httpReq) {
        this._httpReq.abort();
      }
    } }, { key: '_closeLobbySocket', value: function _closeLobbySocket(

    onClose) {
      _closeSocket(this._lobbyWS, onClose);
      this._lobbyWS = null;
    } }, { key: '_closeGameSocket', value: function _closeGameSocket(

    onClose) {
      _closeSocket(this._gameWS, onClose);
      this._gameWS = null;
    } }, { key: 'room', get: function get() {return this._room;} /**
                                                                  * 获取当前玩家
                                                                  * @return {Player}
                                                                  * @readonly
                                                                  */ }, { key: 'player', get: function get() {return this._player;} /**
                                                                                                                                     * 获取房间列表
                                                                                                                                     * @return {Array.<LobbyRoom>}
                                                                                                                                     * @readonly
                                                                                                                                     */ }, { key: 'lobbyRoomList', get: function get() {return this._lobbyRoomList;} }]);return Play;}(EventEmitter);

/**
 * 接收组枚举
 * @readonly
 * @enum {number}
 */
var ReceiverGroup = {
  /**
                       * 其他人（除了自己之外的所有人）
                       */
  Others: 0,
  /**
              * 所有人（包括自己）
              */
  All: 1,
  /**
           * 主机客户端
           */
  MasterClient: 2 };

/**
 * 创建房间标识
 * @readonly
 * @enum {number}
 */
var CreateRoomFlag = {
  /**
                        * Master 掉线后固定 Master
                        */
  FixedMaster: 1,
  /**
                   * 只允许 Master 设置房间属性
                   */
  MasterUpdateRoomProperties: 2,
  /**
                                  * 只允许 Master 设置 Master
                                  */
  MasterSetMaster: 4 };

var play = new Play();

exports.play = play;
exports.Play = Play;
exports.Region = Region;
exports.Room = Room;
exports.Player = Player;
exports.Event = Event;
exports.ReceiverGroup = ReceiverGroup;
exports.CreateRoomFlag = CreateRoomFlag;
exports.setAdapters = setAdapters;
exports.LogLevel = LogLevel;
exports.setLogger = setLogger;
//# sourceMappingURL=play-node.js.map
