使用cocoscreator + node.js + websocket實現簡單的聊天服務


先上個效果圖:

使用cocoscreator 1.9.1 + node.js + websocket實現,沒有使用socket.io, 全部自己封裝,長連接進行封裝后可以和短連接使用方法一樣,使用簡單,方便以后開發網絡游戲。

1、客戶端:

  主要就是聊天內容的顯示,自動換行和背景擴展,代碼大概如下:

cc.Class({
    extends: cc.Component,

    properties: {
        msgLabel: cc.Label,
        uidLabel: cc.Label,
        msgLayout: cc.Layout,
        msgBg: cc.Node,
        maxLen: 500,
    },

    // LIFE-CYCLE CALLBACKS:

    // onLoad () {},

    start () {
        this.node.runAction(cc.fadeTo(0.5, 255))
    },

    initMsg(msg, uid){
        this.msgLabel.string = msg;
        this.uidLabel.string = uid;

        this.msgLabel.overflow = cc.Label.Overflow.NONE;
        // this.msgBg.width = this.msgLabel.node.width + 10;
        // this.msgBg.height = this.msgLabel.node.height + 10;
        // this.node.height = this.msgBg.height + 40;

        this.scheduleOnce((dt)=>{
            if ( this.msgLabel.node.width >= this.maxLen){
                this.msgLabel.overflow = cc.Label.Overflow.RESIZE_HEIGHT;
                this.msgLabel.node.width = this.maxLen;
            }

            this.msgBg.width = this.msgLabel.node.width + 10;
            this.msgBg.height = this.msgLabel.node.height + 10;
            this.node.height = this.msgBg.height + 40;

        }, 0);

        this.node.opacity = 0;
    }

    // update (dt) {},
});

  網絡部分分成了四層:

    1、socket 封裝基礎的websocket, 這里是最底層,也是真正鏈接的開始

    2、network 控制socket鏈接層,實現各回調接口

    3、netproxy 封裝各服務功能,把長連接變成短連接的請求方式

    4、netprotocols 和服務器協商,確定每個請求的請求體格式和回復格式

  各部分代碼如下:

  GameWebSocket.js:  

/**
 * @enum {number}
 */
var GameWebSocketState = cc.Enum({
    CONNECTING: 1,
    OPEN: 2,
    CLOSING: 3,
    CLOSED: 4
});

/**
 * @interface
 */
var GameWebSocketDelegate = cc.Class({

    onSocketOpen: function () {

    },

    /**
     * 收到了消息
     * @param {string|Uint8Array} data
     */
    onSocketMessage: function (data) {

    },

    onSocketError: function () {

    },

    /**
     * 連接關閉
     * @param {string} reason
     */
    onSocketClosed: function (reason) {

    }
});

/**
 * @interface
 */
var GameWebSocketInterface = cc.Class({

    connect: function () {

    },

    send: function () {

    },

    close: function () {

    },

    getState: function () {

    }
});

var GameWebSocket = cc.Class({
    extends: GameWebSocketInterface,

    properties: {

        /**
         * @type {String} 服務器地址
         */
        _address: null,

        /**
         * @type {GameWebSocketDelegate}
         */
        _delegate: null,

        /**
         * @type {WebSocket}
         */
        _webSocket: null,
    },

    /**
     * @param {string} address 服務器地址
     * @param {GameWebSocketDelegate} delegate 回調接口
     */
    init: function(address, delegate){
        this._address = address;
        this._delegate = delegate;
        this._webSocket = null;
    },

    connect: function () {
        cc.log('connect to '+ this._address);

        var ws = this._webSocket = new WebSocket(this._address);
        ws.onopen = this._delegate.onSocketOpen.bind(this._delegate);
        ws.onmessage = function (param) {
            this._delegate.onSocketMessage(param.data);
        }.bind(this);
        ws.onerror = this._delegate.onSocketError.bind(this._delegate);
        // function({code: Number, reason: String, wasClean: Boolean})}
        ws.onclose = function (param) {
            this._delegate.onSocketClosed(param.reason);
        }.bind(this);
    },

    /**
     * 發送數據
     * @param {string|Uint8Array} stringOrBinary
     */
    send: function (stringOrBinary) {
        this._webSocket.send(stringOrBinary);
    },

    close: function () {
        if (!this._webSocket) {
            return;
        }

        try {
            this._webSocket.close();
        } catch (err) {
            cc.log('error while closing webSocket', err.toString());
        }
        this._webSocket = null;
    },

    getState: function () {
        if (this._webSocket) {
            switch(this._webSocket.readyState){
                case WebSocket.OPEN:
                    return GameWebSocketState.OPEN;
                case WebSocket.CONNECTING:
                    return GameWebSocketState.CONNECTING;
                case WebSocket.CLOSING:
                    return GameWebSocketState.CLOSING;
                case WebSocket.CLOSED:
                    return GameWebSocketState.CLOSED;
            }
        }
        return GameWebSocketState.CLOSED;
    }
});

module.exports = {
    GameWebSocketState: GameWebSocketState,
    GameWebSocketDelegate: GameWebSocketDelegate,
    GameWebSocketInterface: GameWebSocketInterface,
    GameWebSocket: GameWebSocket
};

  GameNetwork.js

/**
 * Created by skyxu on 2018/10/9.
 */

"use strict";

let GameWebSocket = require("./GameWebSocket");
let GameProtocols = require("./GameProtocols");

/**
 * 服務器回復消息狀態,判斷回復消息的各種問題
 */
var response_state = {
    ERROR_OK : '0'
};

/**
 * 請求回調對象,收到服務器回調后的回調方法
 */
var NetworkCallback = cc.Class({

    properties: {

        /**
         * @type {BaseRequest} request
         */
        request: null,

        /**
         * 請求回調對方法
         */
        callback: null
    },

    /**
     * @param {BaseRequest} request
     * @param {function(BaseResponse): boolean} callback
     */
    init: function (request, callback) {
        this.request = request;
        this.callback = callback;
    }
});


let GameNetwork = cc.Class({
    extends: GameWebSocket.GameWebSocketDelegate,

    ctor: function() {
        this._socket = null;

        this._delegate = null;

        /**
         * 每次發送請求,都需要有一個唯一的編號
         * @type {number}
         * @private
         */
        this._requestSequenceId = 0;

        /**
         * 接受服務器主動下發的response回調
         * key 表示BaseResponse.act
         * @type {Object.<string, function(object.<string, *>)>}
         */
        this.pushResponseCallback = {};

        /**
         * 根據seq保存Request和其callback,以便在收到服務器的響應后回調
         * @type {Object.<int, NetworkCallback>}
         * @private
         */
        this._networkCallbacks = {};
    },

    setDelegate: function (delegate) {
        this._delegate = delegate;
    },

    /**
     * 注冊服務器主動推送的response 回調
     */
    registerPushResponseCallback : function(act, callback){
        this.pushResponseCallback[act] = callback;
    },

    /**
     * 判斷socket已連接成功,可以通信
     * @returns {boolean}
     */
    isSocketOpened: function(){
        return (this._socket && this._socket.getState() == GameWebSocket.GameWebSocketState.OPEN);
    },

    isSocketClosed: function () {
        return this._socket == null;
    },

    /**
     * 啟動連接
     */
    connect: function (url) {
        cc.log("webSocketUrls=" + url);
        this._requestSequenceId = 0;
        this._socket = new GameWebSocket.GameWebSocket();
        this._socket.init(url, this);
        this._socket.connect();
    },

    closeConnect: function () {
        if(this._socket){
            this._socket.close();
        }
    },

    onSocketOpen: function () {
        cc.log('Socket:onOpen');
        if(this._delegate && this._delegate.onNetworkOpen){
            this._delegate.onNetworkOpen();
        }
    },

    onSocketError: function () {
        cc.log('Socket:onError');
    },

    onSocketClosed: function (reason) {
        cc.log('Socket:onClose', reason);
        if (this._socket) {
            this._socket.close();
        }
        this._socket = null;

        if(this._delegate && this._delegate.onNetworkClose){
            this._delegate.onNetworkClose();
        }
    },

    onSocketMessage: function (msg) {
        this._onResponse(msg);
    },

    _onResponse: function(responseData){
        cc.log('response->resp:', responseData);
        var responseJson = JSON.parse(responseData);
        var responseClass = GameProtocols.response_classes[responseJson.act];
        /**
         * @type {object.<BaseResponse>}
         */
        var response = new responseClass();
        response.loadData(responseJson.data);
        response.act = responseJson.act;
        response.seq = responseJson.seq;
        response.err = responseJson.err;
        response.ts = responseJson.ts;

        // 如果指定了回調函數,先回調
        var ignoreError = false;
        if(response.seq != -1){
            // 處理服務器推送消息
            var pushCallback = this.pushResponseCallback[response.act];
            if(pushCallback){
                pushCallback(response);
            }

            // request回調
            var callbackObj = this._networkCallbacks[response.seq];
            if(callbackObj){
                ignoreError = callbackObj.callback(response);
                // try {
                //     ignoreError = callbackObj.callback(response);
                // } catch (err) {
                //     cc.log(err + " error in response callback of " + response.act);
                // } finally {
                //     delete this._networkCallbacks[response.seq];
                // }
            }
        }

        //有錯,且不忽略,則統一處理錯誤
        if(response.err && response.err != response_state.ERROR_OK && !ignoreError){
            if (response.is_async) {  // 異步請求,如果出錯了,應該需要重新登錄
                // todo 重新登錄?或者重新同步數據?
            } else {  // 同步請求,如果出錯了,需要顯示錯誤信息
                // todo 顯示錯誤
                var msg = responseJson.msg;
                cc.log('server err ' + msg);
            }
        }
    },

    /**
     * 向服務器發送請求。
     *
     * 如果提供了callback,在收到response后會被回調。如果response是一個錯誤(status!=ERR_OK),則需要決定由誰來負責處理錯誤。
     * 如果callback中已經對錯誤進行了處理,應該返回true,這樣會忽略該錯誤。否則應該返回false,則負責處理該錯誤。
     *
     * 特別注意:如果這是一個異步(is_async)請求,且出錯,一般來講應該重新登錄/同步。但是如果callback返回了true,不會進行
     * 任何處理,也就是不會重新登錄/同步。請小心確定返回值。
     *
     * @param {object.<BaseRequest>}
     * @param {function(BaseResponse): boolean=} opt_callback 回調函數。出錯的情況下,如果返回true,則不會再次處理錯誤。
     */
    sendRequest: function (request, opt_callback) {
        // 每個請求的seq應該唯一,且遞增
        request.seq = ++this._requestSequenceId;

        //生成NetworkCallback對象,綁定請求seq和回調方法
        if(opt_callback){
            this._networkCallbacks[request.seq] = new NetworkCallback();
            this._networkCallbacks[request.seq].init(request, opt_callback);
        }
        this._sendSocketRequest(false, request);
    },

    /**
     * sendRequest的不發送data字段
     */
    sendRequestNoData: function (request, opt_callback) {
        // 每個請求的seq應該唯一,且遞增
        request.seq = ++this._requestSequenceId;

        //生成NetworkCallback對象,綁定請求seq和回調方法
        if(opt_callback){
            this._networkCallbacks[request.seq] = new NetworkCallback();
            this._networkCallbacks[request.seq].init(request, opt_callback);
        }
        this._sendSocketRequest(true, request);
    },

    /**
     * @param {Boolean} isNoData
     * @param {object.<BaseRequest>} req
     */
    _sendSocketRequest: function (isNoData, req) {
        cc.assert(this._socket);

        if (this.isSocketOpened()){
            //通過json的方法生成請求字符串
            var msg = null;
            if(isNoData){
                msg = JSON.stringify({seq:req.seq, act:req.act});
            }else{
                msg = JSON.stringify({seq:req.seq, act:req.act, data:req});
            }
            cc.log("WebSocketDelegate::send->" + msg);
            this._socket.send(msg);
        } else{
            // todo
        }
    }
});

module.exports = GameNetwork;

  GameProtocols.js

/**
 * Created by skyxu on 2018/10/9.
 */

"use strict";

/**
 * 消息基類對象,請求消息BaseRequest, 回調消息BaseResponse都繼承BaseProtocol
 */
let BaseProtocol = cc.Class({
    ctor: function () {
        /**
         * 請求動作類型
         */
        this.act = '';

        /**
         * 每個請求的sequence_id應該唯一
         */
        this.seq = 0;

        /**
         * 錯誤代碼,0為正常
         */
        this.err = 0;

        /**
         * 是否需要等待服務器回調
         */
        this.is_async = false;
    }
});

/**
 * 請求消息基類,客戶端的請求都繼承這個類
 */
let BaseRequest = cc.Class({
    extends: BaseProtocol
});

/**
 * 服務器返回的消息對應的對象,包含返回數據,一般和BaseRequest成對使用
 * @class BaseResponse
 * @extends BaseProtocol
 */
let BaseResponse = cc.Class({
    extends: BaseProtocol,

    /**
     * 讀取返回數據,設置BaseResponse對象
     */
    loadData: function (data) {
        var key;
        for (key in data) {
            if(!this.hasOwnProperty(key)){
                continue;
            }

            if(data[key] !== undefined && data[key] !== null){
                this[key] = data[key];
            }
        }
    }
});

let HeartRequest = cc.Class({
    extends: BaseRequest,
    ctor(){
        this.act = 'heart';
        this.t = -1;    // 發送時間
    }
});

let HeartResponse = cc.Class({
    extends: BaseResponse,

    ctor(){
        this.act = 'heart';
        this.t = -1;
    }
});

let ChatRequest = cc.Class({
    extends: BaseRequest,
    ctor(){
        this.act = 'chat';
        this.msg = '';
        this.uid = '';
    }
});

let ChatResponse = cc.Class({
    extends: BaseResponse,
    ctor(){
        this.act = 'chat';
        this.msg = '';
        this.uid = '';
    }
});

let LoginRequest = cc.Class({
    extends: BaseRequest,

    ctor: function () {
        this.act = 'login';

        /**
         * facebook用戶的accessToken,或游客的UUID
         */
        this.token = '';

        /**
         * token來源,默認0:游客,1:facebook
         */
        this.origin = 0;

        /**
         * 平台: 必須為以下幾種之一:android/ios/winphone/pc
         */
        this.os = '';

        /**
         * 平台系統版本
         */
        this.osVersion = '';

        /**
         * 設備產品型號, 示例 iPhone8,2, SM-G 9280
         */
        this.deviceModel = '';

        /**
         * 渠道ID
         */
        this.channelId = 0;

        /**
         * Ios設備廣告標示符
         */
        this.idfa = '';

        /**
         * 安卓設備id
         */
        this.androidId = '';

        /**
         * Google廣告平台賬號,安裝了google play的設備可取到
         */
        this.googleAid = '';

        /**
         * 應用版本號
         */
        this.appVersion = '';

        /**
         * 取package name或者bundle id
         */
        this.packName = '';


        /**
         * 設備語言
         * @type {string}
         */
        this.language = '';

        this.locale = "";

    }
});

let LoginResponse = cc.Class({
    extends: BaseResponse,

    ctor: function () {
        this.act = 'login';

        /**
         * 游客第一次登錄時返回的token,需要客戶端保存
         */
        this.token = '';

        /**
         * 離體力下次恢復點的剩余時間秒數
         * @type {number}
         */
        this.spStepLeftTime = 0;

        /**
         * 體力恢復周期
         * @type {Number}
         */
        this.spInterval = 0;

        /**
         * 農場每天產出量,產出未解鎖時為-1
         * @type {number}
         */
        this.farmDailyOut = -1;

        /**
         * 農場已產出量
         * @type {number}
         */
        this.farmCoins = 0;

        /**
         * 農場產出間隔
         * @type {number}
         */
        this.farmInterval = null;

        /**
         * 用json object表示的一個player對象,字段說明參見player json對象
         */
        this.me = {};

        /**
         * 建築數據數組
         * @type {Array}
         */
        this.buildings = [];

        /**
         * 農民數據數組
         * @type {Array}
         */
        this.farms = [];

        /**
         * 富豪數據
         */
        this.cashking = {};

        /**
         * 行星配置
         */
        this.planetConf = {};

        /**
         * 農民配置
         */
        this.farmConfList = [];

        /**
         * 其他配置
         */
        this.settingConf = {};

        /**
         * 好友數據
         */
        this.friends = [];

        /**
         * 好友通緝的目標列表
         */
        this.helpWantList = [];

        /**
         * 郵件消息列表
         */
        this.newsList = [];

        /**
         * 復仇列表
         */
        this.revengeList = [];

        /**
         * 商品信息
         * @type {Array}
         */
        this.rechargeConfs = [];

        /**
         * 總島數
         * @type {Number}
         */
        this.planetConfListSize = 0;

        /**
         * 他人行星信息對象,僅在轉到fire斷線重新登錄時有效
         * @type {Object}
         */
        this.fireTarget = null;

        /**
         * 他人行星信息對象列表,僅在轉到steal斷線重新登錄時有效
         * @type {Array}
         */
        this.stealTarget = null;
    }
});

let LogoutRequest = cc.Class({
    extends: BaseRequest,

    ctor: function () {
        this.act = 'logout';
    }
});

let LogoutResponse = cc.Class({
    extends: BaseResponse,

    ctor: function () {
        this.act = 'logout';
    }
});

/**
 * 綁定fb賬號
 * @extends BaseRequest
 */
let BindFacebookRequest = cc.Class({
    extends: BaseRequest,

    ctor: function () {
        this.act = 'bindFb';

        /**
         * facebook用戶的accessToken,或游客的UUID
         */
        this.token = '';
    }
});
/**
 * 綁定fb賬號
 * @extends BaseResponse
 */
let BindFacebookResponse = cc.Class({
    extends: BaseResponse,

    ctor: function () {
        this.act = 'bindFb';

        /**
         * fb數據
         */
        this.me = 0;

        /**
         * fb好友
         */
        this.friends = 0;
    }
});

let SpinRequest = cc.Class({
    extends: BaseRequest,

    ctor: function () {
        this.act = 'spin';

        /**
         * 倍數
         * @type {Number}
         */
        this.x = 1;
    }
});

let SpinResponse = cc.Class({
    extends: BaseResponse,

    ctor: function () {
        this.act = 'spin';

        /**
         * 搖中的轉盤ID
         */
        this.hit = 0;

        /**
         * 轉到護盾,但護盾已滿時,存在
         * @type {number}
         */
        this.shieldfull = 0;

        /**
         * 玩家數據對象
         */
        this.me = {};

        /**
         * 他人行星信息對象,僅在轉到fire時有效
         * @type {*}
         */
        this.fireTarget = {};

        /**
         * 偷取對象數據
         */
        this.stealTarget = [];

        /**
         * 離體力下次恢復點的剩余時間秒數
         * @type {number}
         */
        this.spStepLeftTime = 0;

        /**
         * 體力恢復周期
         * @type {Number}
         */
        this.spInterval = 0;

        /**
         * 倍數
         * @type {Number}
         */
        this.x = 1;
    }
});

/**
 * 獲取排名
 * @extends BaseRequest
 */
let RankRequest = cc.Class({
    extends: BaseRequest,

    ctor: function () {
        this.act = 'rankboard';

        /**
         * 請求動作類型{ 0全部,1本地,2好友 }
         * @type {int}
         */
        this.type = 0;
    }
});
/**
 * 獲取排名
 * @extends BaseResponse
 */
let RankResponse = cc.Class({
    extends: BaseResponse,

    ctor: function () {
        this.act = 'rankboard';

        /**
         *  我的排名
         */
        this.myRank = 0;

        /**
         * 排名玩家數據
         */
        this.men = [];
    }
});


//push------------------------------------------------------------------------------

/**
 * 推送消息 被攻擊
 * @extends BaseResponse
 */
var PushAttackedResponse = cc.Class({
    extends: BaseResponse,

    ctor: function () {
        this.act = 'attacked';

        /**
         * 玩家更新數據
         */
        this.me = null;

        /**
         * 建築數據
         */
        this.building = null;

        /**
         * 敵人
         */
        this.hatredman = null;

        /**
         * 消息
         */
        this.news = null;
    }
});


/**
 * 推送消息 推送消息好友已贈送體力
 * @extends BaseResponse
 */
var PushSendSpResponse = cc.Class({
    extends: BaseResponse,

    ctor: function () {
        this.act = 'sendSpNotify';

        /**
         * 好友對象
         */
        this.friend = null;
    }
});

/**
 * 推送消息 推送消息好友已領取贈送的體力
 * @extends BaseResponse
 */
var PushTakeSpResponse = cc.Class({
    extends: BaseResponse,

    ctor: function () {
        this.act = 'takeSpNotify';

        /**
         * 好友對象
         */
        this.friend = null;
    }
});

/**
 * 推送消息 同步好友信息
 * @extends BaseResponse
 */
var PushSyncFriendInfo = cc.Class({
    extends: BaseResponse,

    ctor: function () {
        this.act = 'friendInfoSync';

        /**
         * 好友
         */
        this.friend = null;
    }
});

/**
 * 推送消息 新增好友
 * @extends BaseResponse
 */
var PushAddNewFriend = cc.Class({
    extends: BaseResponse,

    ctor: function () {

        this.act = 'newFriend';

        /**
         * 好友
         */
        this.friend = null;

        /**
         * 消息
         */
        this.news = null;
    }
});

/**
 * debug回調
 * @extends BaseRequest
 */
let DebugChangeMeRequest = cc.Class({
    extends: BaseRequest,

    ctor: function () {

        this.act = "cmdTest";                    //請求動作類型
        this.cmd = "";
        //  "player coins add 100", cmd格式:player field value 或者 player field add value
        //  Building field [add] value where playerId value type value
    }

});
/**
 * debug回調
 * @extends BaseResponse
 */
let DebugChangeMeResponse = cc.Class({
    extends: BaseResponse,

    ctor: function () {
        this.act = "cmdTest";

        /**
         * 玩家數據
         * @type {Object}
         */
        this.me = {};

        /**
         * 體力恢復周期
         * @type {Number}
         */
        this.spInterval = null;

        /**
         * 體力恢復剩余時間
         * @type {Number}
         */
        this.spStepLeftTime = null;

        /**
         * 存錢罐速度
         * @type {Number}
         */
        this.farmDailyOut = null;

        /**
         * 存錢罐可回收金幣
         * @type {Number}
         */
        this.farmCoins = null;

        /**
         * 存錢罐回收周期
         * @type {Number}
         */
        this.farmInterval = null;

        /**
         * 島嶼建築數據
         * @type {Array}
         */
        this.buildings = null;
    }
});

let response_classes = {
    login: LoginResponse,
    logout: LogoutResponse,
    spin: SpinResponse,
    bindFb: BindFacebookResponse,
    rankboard: RankResponse,
    heart: HeartResponse,
    chat: ChatResponse,

    //push
    attacked: PushAttackedResponse,
    sendSpNotify: PushSendSpResponse,
    takeSpNotify: PushTakeSpResponse,
    newFriend: PushAddNewFriend,
    friendInfoSync: PushSyncFriendInfo,

    // debug
    cmdTest: DebugChangeMeResponse,
};

module.exports = {
    LoginRequest: LoginRequest,
    LoginResponse: LoginResponse,
    LogoutRequest: LogoutRequest,
    LogoutResponse: LogoutResponse,
    SpinRequest: SpinRequest,
    SpinResponse: SpinResponse,
    BindFacebookRequest: BindFacebookRequest,
    BindFacebookResponse: BindFacebookResponse,
    RankRequest: RankRequest,
    RankResponse: RankResponse,
    HeartRequest: HeartRequest,
    HeartResponse: HeartResponse,
    ChatRequest: ChatRequest,
    ChatResponse: ChatResponse,

    // debug
    DebugChangeMeRequest: DebugChangeMeRequest,
    DebugChangeMeResponse: DebugChangeMeResponse,

    //push消息
    PushAttackedResponse: PushAttackedResponse,
    PushSendSpResponse: PushSendSpResponse,
    PushTakeSpResponse: PushTakeSpResponse,
    PushAddNewFriend: PushAddNewFriend,
    PushSyncFriendInfo: PushSyncFriendInfo,

    response_classes: response_classes
};

  NetProxy.js

/**
* Created by skyxu on 2018/10/9.
*/

"use strict";

let GameNetwork = require("./GameNetwork");
let GameProtocols = require("./GameProtocols");

let GAME_SERVER_URL = 'ws://127.0.0.1:3000';
// GAME_SERVER_URL = 'wss://echo.websocket.org';

let NetProxy = cc.Class({
ctor: function () {
this.network = null;
this._cachePushCallback = [];
},

init: function () {
this.network = new GameNetwork();
this.network.setDelegate(this);
this.initPushCallback();
},

connect: function () {
this.network.connect(GAME_SERVER_URL);
},

closeConnect: function () {
this.network.closeConnect();
},

isNetworkOpened: function () {
return this.network.isSocketOpened();
},

isNetworkClosed: function () {
return this.network.isSocketClosed();
},

onNetworkOpen: function () {
Global.eventMgr.emit(Global.config.EVENT_NETWORK_OPENED);
},

onNetworkClose: function () {
Global.eventMgr.emit(Global.config.EVENT_NETWORK_CLOSED);
},

/**
* 注冊push回調接口
*/
initPushCallback: function () {
let self = this;
let pushCallback = function (resp) {
self.pushCallback(resp);
};

this.network.registerPushResponseCallback('chat', pushCallback);


// let pushCallback = function(response){
// if(Util.DNN(farm.game) && farm.game.loginSuccess){
// this.dealCachePush();
// this.pushCallback(response);
// }else{
// this._cachePushCallback.push(response);
// }
// }.bind(this);

// this.network.registerPushResponseCallback('attacked', pushCallback);
// this.network.registerPushResponseCallback('acceptWantHelp', pushCallback);
// this.network.registerPushResponseCallback('sendSpNotify', pushCallback);
// this.network.registerPushResponseCallback('takeSpNotify', pushCallback);
// this.network.registerPushResponseCallback('wanted', pushCallback);
// this.network.registerPushResponseCallback('incomplete', pushCallback);
// this.network.registerPushResponseCallback('newFriend', pushCallback);
// this.network.registerPushResponseCallback('news', pushCallback);
// this.network.registerPushResponseCallback('hatredInfoSync', pushCallback);
// this.network.registerPushResponseCallback('friendInfoSync', pushCallback);
},

/**
* 處理緩存push
*/
dealCachePush: function () {
// if(this._cachePushCallback.length > 0){
// for(var i = 0; i < this._cachePushCallback.length; i++){
// this.pushCallback(this._cachePushCallback[i]);
// }
// }
// this._cachePushCallback = [];
},

beatHeart: function (callback) {
let req = new GameProtocols.HeartRequest();
req.t = Date.now();
this.network.sendRequest(req, callback);
},

chat: function (msg) {
let req = new GameProtocols.ChatRequest();
let uid = cc.sys.localStorage.getItem("chat_uid");
req.uid = uid;
req.msg = msg;
this.network.sendRequest(req);
},

/**
* Facebook或者游客登錄接口
* @param {Object.<LoginOriginType>} origin
* @param token
*/
login: function (origin, token) {
// let req = new GameProtocols.LoginRequest();
// if(token) req.token = token;
// req.origin = origin;
// req.os = cc.sys.os;
// req.osVersion = cc.sys.osVersion;
// // req.language = cc.sys.language;//farm.FarmPlatformHelper.jsToOc(farm.FarmPlatformHelper.JSB_EVENT_JTO_GetCurrentLanguage);
// /*
// req.deviceModel = '';
// req.channelId = 0;
// req.idfa = '';
// req.androidId = '';
// req.googleAid = '';
// req.appVersion = '';
// req.packName = '';
// */
// let callback = function (resp) {
// if(resp.err != 0){
// Global.eventMgr.emit(farm.game.gmConst.SP_EVENT_LOGIN_FAILED, resp);
// return;
// }
// if(resp.token && resp.token.length > 0){
// farm.localStorage.setItem(farm.game.gmConst.GLS_KEY_GUEST_TOKEN, resp.token);
// }
// farm.localStorage.removeItem(farm.game.gmConst.GLS_KEY_IS_LOGOUT);
//
// //
// farm.game.initConfig(resp);
// farm.game.initData(resp);
// farm.game.loginSuccess = true;
//
// // js 調取其他平台的sdk,傳過去玩家id
// farm.FarmPlatformHelper.jsToOc(farm.FarmPlatformHelper.JSB_EVENT_JTO_setSessionWithUid, farm.game.player.id.toString());
// //
//
// //登錄
// farm.eventManager.emit(farm.game.gmConst.SP_EVENT_LOGIN_SUCCESS);
// };
// this.network.sendRequest(req, callback);

},

/**
* Facebook或者游客登出
*/
logout: function () {
// let req = new GameProtocols.LogoutRequest();
// this.network.sendRequest(req, function (resp) {
// if(resp.err != 0){
// cc.log("網絡請求---LogoutRequest 失敗");
// farm.eventManager.emit(farm.game.gmConst.SP_EVENT_LOGOUT_FAILED);
// return;
// }
// cc.log("網絡請求---LogoutRequest 成功");
// Global.eventMgr.emit(farm.game.gmConst.SP_EVENT_LOGOUT_SUCCESS);
// });
},

/**
* 綁定fb賬號
* @param {String} token
*/
bindFacebook: function (token) {
// let req = new GameProtocols.BindFacebookRequest();
// req.token = token;
// let callback = function (resp) {
// //綁定過得邏輯
// if(resp.err == farm.game.gmConst.ERROR_USER_HAS_REGISTERED){
// cc.log("網絡請求---BindFacebookRequest 已綁定");
// farm.eventManager.emit(farm.game.gmConst.SP_EVENT_HAS_BIND_FACEBOOK);
// return;
// }
// //綁定失敗
// if(resp.err != 0){
// cc.log("網絡請求---BindFacebookRequest 失敗");
// farm.eventManager.emit(farm.game.gmConst.SP_EVENT_BIND_FACEBOOK_FAILED);
// return;
// }
// //綁定成功
// cc.log("網絡請求---BindFacebookRequest 成功");
// if(resp.me){
// farm.game.player.parse(resp.me);
// }
// if(resp.friends){
// farm.game.initFriends(resp.friends);
// }
// //綁定成功后刪除本地token
// farm.localStorage.removeItem(farm.game.gmConst.GLS_KEY_GUEST_TOKEN);
// farm.eventManager.emit(farm.game.gmConst.SP_EVENT_BIND_FACEBOOK_SUCCESS);
// };
// this.network.sendRequest(req, callback);
},

/**
* 啟動轉盤
*/
spin: function (x) {
// let req = new GameProtocols.SpinRequest();
// if(farm.util.isNumber(x)){
// req.x = x;
// }
// var callback = function (resp) {
// if(resp.err != 0){
// cc.log("網絡請求---spin 失敗");
// farm.eventManager.emit(farm.game.gmConst.SP_EVENT_SPIN_FAILED, resp);
// return;
// }
// cc.log("網絡請求---spin 成功");
// farm.game.player.parse(resp.me);
// farm.game.spTimer.updateSpTime(resp.spStepLeftTime, resp.spInterval);
// farm.eventManager.emit(farm.game.gmConst.SP_EVENT_SPIN_SUCCESS, resp);
// };
// this.network.sendRequest(req, callback);
},

/**
* 獲取排名
* @param {Number} rankType 0全部,1本地,2好友
*/
getRank: function (rankType) {
// let req = new GameProtocols.RankRequest();
// req.type = rankType;
// let callback = function (resp) {
// if(resp.err != 0){
// cc.log("網絡請求---getRank 失敗");
// Global.eventMgr.emit(farm.game.gmConst.SP_EVENT_GET_RANK_FAILED, resp);
// return;
// }
// cc.log("網絡請求---getRank 成功");
// // todo 暫定排名類型
// resp._rankType = rankType;
// //farm.game.initLeaderBoardArray(rankType, resp.myRank, resp.men);
// if(rankType == 2 && resp.men){
// farm.game.updateFriends(resp.men);
// resp.men = farm.game.sortFriendsByStar();
// }
// Global.eventMgr.emit(farm.game.gmConst.SP_EVENT_GET_RANK_SUCCESS, resp);
// };
// this.network.sendRequest(req, callback);
},


//push回調------------------------------------------------------------------------------

/**
* 推送回調
*/
pushCallback: function (response) {
switch (response.act){
case "sendSpNotify":
case "takeSpNotify":
case "friendInfoSync":
this.pushFriendSendTakeSp(response);
break;
case "stole":
this.pushStole(response);
break;
case "attacked":
this.pushAttacked(response);
break;
case "newFriend":
this.pushAddNewFriend(response);
break;
case "chat":
this.pushChat(response);
break;
}
},
/**
* 好友間互贈體力推送
* @param {PushSendSpResponse|PushTakeSpResponse} resp
*/
pushFriendSendTakeSp: function (resp) {
// cc.log("網絡請求---push--- pushFriendSendTakeSp 成功");
// if(resp.friend) farm.game.updateFriends(resp.friend);
// farm.eventManager.emit(farm.game.gmConst.SP_PUSH_EVENT_UPDATE_FRIEND);
},

/**
* 被偷
* @param {PushStolenResponse} resp
*/
pushStole: function (resp) {
// cc.log("網絡請求---push--- pushStole 成功");
// if(resp.me) farm.game.player.parse(resp.me);
// //if(resp.building) farm.game.buildings[resp.building.type].parse(resp.building);
// if(resp.hatredman && !farm.game.getHelpWant(resp.hatredman.id)){
// farm.game.addEnemy(resp.hatredman);
// }
// if(resp.news){
// resp.news = farm.game.addNews(resp.news);
// }
// farm.eventManager.emit(farm.game.gmConst.SP_PUSH_EVENT_BE_STOLE_SUCCESS, resp);
},

/**
* 被攻擊
* @param {PushAttackedResponse} resp
*/
pushAttacked: function (resp) {
// cc.log("網絡請求---push--- pushAttacked 成功");
// if(resp.me) {
// farm.game.player.parse(resp.me);
// farm.game.dataUpdater.updateStar();
// }
// if(resp.building) farm.game.buildings[resp.building.type].parse(resp.building);
// if(resp.hatredman){
// farm.game.addBadass(resp.hatredman);
// if(!farm.game.getHelpWant(resp.hatredman.id)){
// farm.game.addEnemy(resp.hatredman);
// }
// }
// if(resp.news){
// resp.news = farm.game.addNews(resp.news);
// }
// farm.eventManager.emit(farm.game.gmConst.SP_PUSH_EVENT_BE_ATTACK_SUCCESS, resp);
},

/**
* 新增好友
* @param {PushAddNewFriend} resp
*/
pushAddNewFriend: function (resp) {
// cc.log("網絡請求---push--- pushAddNewFriend 成功");
// if(resp.friend){
// resp.friend = farm.game.addFriend(resp.friend);
// }
// if(resp.news){
// resp.news = farm.game.addNews(resp.news);
// }
// farm.eventManager.emit(farm.game.gmConst.SP_PUSH_EVENT_ADD_FRIEND_SUCCESS, resp);
},

pushChat: function (resp) {
Global.eventMgr.emit(Global.config.EVENT_CHAT, resp);
},

/**
* debug調試請求
* @param {String} name
*/
debug_addCoins: function (name) {
var req = new GameProtocols.DebugChangeMeRequest();
if (name === "btnAddCoins") {
req.cmd = "player coins add 100000000";
} else if (name === "btnClearCoins") {
req.cmd = "player coins 0";
} else if (name === "btnAddEnergy") {
req.cmd = "player sp add 10";
} else if (name === "btnClearEnergy") {
req.cmd = "player sp 0";
} else if (name == "btnAddWp") {
req.cmd = "player wp add 10";
} else if (name == "btnClearWp") {
req.cmd = "player wp 0";
} else if (name == "btnUnwrap"){
req.cmd = "player fbuid null";
} else if (name == "btnWizard1"){
req.cmd = "player wizard1 0";
} else if (name == "btnWizard2"){
req.cmd = "player wizard2 0";
} else if (name == "btnClearShield"){
req.cmd = "player shield 0";
} else if (name == "btnSpEc"){
req.cmd = "SpEc stepInterval 60000";
} else if (name == "btnFarmEc"){
req.cmd = "FarmEc stepInterval 60000";
} else if (name == "btnSpEcBack"){
req.cmd = "SpEc stepInterval 3600000";
} else if (name == "btnFarmBack"){
req.cmd = "FarmEc stepInterval 86400000";
} else if (name == "btnUpdateBuild"){
req.cmd = "Building lv 5";
} else {
req.cmd = name;
}
// var callback = function (resp) {
// if (resp.err != 0) {
// return;
// }
// farm.game.player.parse(resp.me);
// farm.game.spTimer.updateSpTime(resp.spStepLeftTime, resp.spInterval);
// farm.game.dataUpdater.updateCoin();
// farm.game.dataUpdater.updateSp();
// farm.game.dataUpdater.updateShield();
// farm.game.dataUpdater.updateStar();
// //
// if((req.cmd == "FarmEc stepInterval 60000" || req.cmd == "FarmEc stepInterval 86400000")
// && farm.util.isNumber(resp.farmDailyOut)
// && farm.util.isNumber(resp.farmCoins)){
// farm.game.piggyBankTimer.init(resp.farmDailyOut, resp.farmCoins, resp.farmInterval);
// }
// if(req.cmd == "SpEc stepInterval 60000" || req.cmd == "SpEc stepInterval 3600000"){
// farm.game.spTimer.updateSpTime(resp.spStepLeftTime, resp.spInterval);
// }
// if(resp.buildings){
// for(var i = 0; i < resp.buildings.length; ++i){
// farm.game.buildings[i].parse(resp.buildings[i]);
// }
// farm.eventManager.emit(farm.game.gmConst.SP_EVENT_UPGRADE_BUILDING_SUCCESS, resp);
// farm.eventManager.emit(farm.game.gmConst.SP_DEBUG_EVENT_BUILD_TO_24_SUCCESS, resp);
// }
// };
// this.network.sendRequest(req, callback);
},
});

module.exports = NetProxy;

2、服務端

 使用express + express-ws組件,調用WSRouter的init初始化連接即可,

let expressWS = require('express-ws');
let work = require('./work');

let wsRouter = null;

function WSRouter(app, server){
    this.app = app;
    this.server = server;
    this.clients = [];
    expressWS(app, server);

    this.listenClientConnection = ()=>{
        app.ws('/', (socket, req)=>{
            console.log('client connect to server successful.');
            this.clients.push(socket);
            console.log('clients: ' + this.clients.length);
            socket.on('message', (msg)=>{
                console.log('on message: ' + msg);
                work(this, socket, msg);
            });

            socket.on('close', (msg)=>{
                console.log('on close: ' + msg);
                for (let index=0; index<this.clients.length; index++){
                    if (this.clients[index] == socket){
                        this.clients.splice(index,1);
                    }
                }
                console.log('clients: ' + this.clients.length);
            });

            socket.on('error', (err)=>{
                console.log('on error: ' + error);
            });
        })
    }
}


module.exports = {
    init: function(app, server){
        if (wsRouter == null){
            wsRouter = new WSRouter(app, server);
        }
        return wsRouter;
    }
}
module.exports = function(wsRouter, ws, msg){
    let msgObj = JSON.parse(msg);
    switch (msgObj.act){
        case 'heart':{
            msgObj.data.t = Date.now();
            ws.send(JSON.stringify(msgObj));
            break;
        }
        case 'chat': {
            for (let w of wsRouter.clients){
                w.send(msg);
            }
            break;
        }

        default:
            break;
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM