Pomelo库在CocosCreator中如何高效实现跨平台游戏开发?
- 内容介绍
- 文章标签
- 相关推荐
本文共计3956个文字,预计阅读时间需要16分钟。
Pomelo 客户端模块,包含:- 暴露 `Emitter` 对象- 初始化新的 `Emitter`- 公开 API:`Emitter` 构造函数
Pomelo客户端(function(){
/**
* Expose `Emitter`.
*/
// module.exports = Emitter;
/**
* Initialize a new `Emitter`.
*
* @api public
*/
function Emitter(obj) {
if (obj) return mixin(obj);
}
/**
* Mixin the emitter properties.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
function mixin(obj) {
for (let key in Emitter.prototype) {
obj[key] = Emitter.prototype[key];
}
return obj;
}
/**
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
(this._callbacks[event] = this._callbacks[event] || [])
.push(fn);
return this;
};
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.once = function(event, fn){
let self = this;
this._callbacks = this._callbacks || {};
function on() {
self.off(event, on);
fn.apply(this, arguments);
}
on.fn = fn;
this.on(event, on);
return this;
};
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
// all
if (0 === arguments.length) {
this._callbacks = {};
return this;
}
// specific event
let callbacks = this._callbacks[event];
if (!callbacks) return this;
// remove all handlers
if (1 === arguments.length) {
delete this._callbacks[event];
return this;
}
// remove specific handler
let cb;
for (let i = 0; i < callbacks.length; i++) {
cb = callbacks[i];
if (cb === fn || cb.fn === fn) {
callbacks.splice(i, 1);
break;
}
}
return this;
};
/**
* Emit `event` with the given args.
*
* @param {String} event
* @return {Emitter}
*/
Emitter.prototype.emit = function(event){
this._callbacks = this._callbacks || {};
let args = [].slice.call(arguments, 1)
, callbacks = this._callbacks[event];
if (callbacks) {
callbacks = callbacks.slice(0);
for (let i = 0, len = callbacks.length; i < len; ++i) {
callbacks[i].apply(this, args);
}
}
return this;
};
/**
* Return array of callbacks for `event`.
*
* @param {String} event
* @return {Array}
* @api public
*/
Emitter.prototype.listeners = function(event){
this._callbacks = this._callbacks || {};
return this._callbacks[event] || [];
};
/**
* Check if this emitter has `event` handlers.
*
* @param {String} event
* @return {Boolean}
* @api public
*/
Emitter.prototype.hasListeners = function(event){
return !! this.listeners(event).length;
};
if(typeof(window) !== "undefined") {
window.EventEmitter = Emitter;
}
})();
(function (exports, ByteArray, global) {
let Protocol = exports;
let PKG_HEAD_BYTES = 4;
let MSG_FLAG_BYTES = 1;
let MSG_ROUTE_CODE_BYTES = 2;
let MSG_ID_MAX_BYTES = 5;
let MSG_ROUTE_LEN_BYTES = 1;
let MSG_ROUTE_CODE_MAX = 0xffff;
let MSG_COMPRESS_ROUTE_MASK = 0x1;
let MSG_TYPE_MASK = 0x7;
let Package = Protocol.Package = {};
let Message = Protocol.Message = {};
Package.TYPE_HANDSHAKE = 1;
Package.TYPE_HANDSHAKE_ACK = 2;
Package.TYPE_HEARTBEAT = 3;
Package.TYPE_DATA = 4;
Package.TYPE_KICK = 5;
Message.TYPE_REQUEST = 0;
Message.TYPE_NOTIFY = 1;
Message.TYPE_RESPONSE = 2;
Message.TYPE_PUSH = 3;
/**
* pomele client encode
* id message id;
* route message route
* msg message body
* socketio current support string
*/
Protocol.strencode = function(str) {
let byteArray = new ByteArray(str.length * 3);
let offset = 0;
for(let i = 0; i < str.length; i++){
let charCode = str.charCodeAt(i);
let codes = null;
if(charCode <= 0x7f){
codes = [charCode];
}else if(charCode <= 0x7ff){
codes = [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];
}else{
codes = [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];
}
for(let j = 0; j < codes.length; j++){
byteArray[offset] = codes[j];
++offset;
}
}
let _buffer = new ByteArray(offset);
copyArray(_buffer, 0, byteArray, 0, offset);
return _buffer;
};
/**
* client decode
* msg String data
* return Message Object
*/
Protocol.strdecode = function(buffer) {
let bytes = new ByteArray(buffer);
let array = [];
let offset = 0;
let charCode = 0;
let end = bytes.length;
while(offset < end){
if(bytes[offset] < 128){
charCode = bytes[offset];
offset += 1;
}else if(bytes[offset] < 224){
charCode = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);
offset += 2;
}else{
charCode = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);
offset += 3;
}
array.push(charCode);
}
return String.fromCharCode.apply(null, array);
};
/**
* Package protocol encode.
*
* Pomelo package format:
* +------+-------------+------------------+
* | type | body length | body |
* +------+-------------+------------------+
*
* Head: 4bytes
* 0: package type,
* 1 - handshake,
* 2 - handshake ack,
* 3 - heartbeat,
* 4 - data
* 5 - kick
* 1 - 3: big-endian body length
* Body: body length bytes
*
* @param {Number} type package type
* @param {ByteArray} body body content in bytes
* @return {ByteArray} new byte array that contains encode result
*/
Package.encode = function(type, body){
let length = body ? body.length : 0;
let buffer = new ByteArray(PKG_HEAD_BYTES + length);
let index = 0;
buffer[index++] = type & 0xff;
buffer[index++] = (length >> 16) & 0xff;
buffer[index++] = (length >> 8) & 0xff;
buffer[index++] = length & 0xff;
if(body) {
copyArray(buffer, index, body, 0, length);
}
return buffer;
};
/**
* Package protocol decode.
* See encode for package format.
*
* @param {ByteArray} buffer byte array containing package content
* @return {Object} {type: package type, buffer: body byte array}
*/
Package.decode = function(buffer){
let offset = 0;
let bytes = new ByteArray(buffer);
let length = 0;
let rs = [];
while(offset < bytes.length) {
let type = bytes[offset++];
length = ((bytes[offset++]) << 16 | (bytes[offset++]) << 8 | bytes[offset++]) >>> 0;
let body = length ? new ByteArray(length) : null;
copyArray(body, 0, bytes, offset, length);
offset += length;
rs.push({'type': type, 'body': body});
}
return rs.length === 1 ? rs[0]: rs;
};
/**
* Message protocol encode.
*
* @param {Number} id message id
* @param {Number} type message type
* @param {Number} compressRoute whether compress route
* @param {Number|String} route route code or route string
* @param {Buffer} msg message body bytes
* @return {Buffer} encode result
*/
Message.encode = function(id, type, compressRoute, route, msg){
// caculate message max length
let idBytes = msgHasId(type) ? caculateMsgIdBytes(id) : 0;
let msgLen = MSG_FLAG_BYTES + idBytes;
if(msgHasRoute(type)) {
if(compressRoute) {
if(typeof route !== 'number'){
throw new Error('error flag for number route!');
}
msgLen += MSG_ROUTE_CODE_BYTES;
} else {
msgLen += MSG_ROUTE_LEN_BYTES;
if(route) {
route = Protocol.strencode(route);
if(route.length>255) {
throw new Error('route maxlength is overflow');
}
msgLen += route.length;
}
}
}
if(msg) {
msgLen += msg.length;
}
let buffer = new ByteArray(msgLen);
let offset = 0;
// add flag
offset = encodeMsgFlag(type, compressRoute, buffer, offset);
// add message id
if(msgHasId(type)) {
offset = encodeMsgId(id, buffer, offset);
}
// add route
if(msgHasRoute(type)) {
offset = encodeMsgRoute(compressRoute, route, buffer, offset);
}
// add body
if(msg) {
offset = encodeMsgBody(msg, buffer, offset);
}
return buffer;
};
/**
* Message protocol decode.
*
* @param {Buffer|Uint8Array} buffer message bytes
* @return {Object} message object
*/
Message.decode = function(buffer) {
let bytes = new ByteArray(buffer);
let bytesLen = bytes.length || bytes.byteLength;
let offset = 0;
let id = 0;
let route = null;
// parse flag
let flag = bytes[offset++];
let compressRoute = flag & MSG_COMPRESS_ROUTE_MASK;
let type = (flag >> 1) & MSG_TYPE_MASK;
// parse id
if(msgHasId(type)) {
let m = parseInt(bytes[offset]);
let i = 0;
do{
let m = parseInt(bytes[offset]);
id = id + ((m & 0x7f) * Math.pow(2,(7*i)));
offset++;
i++;
}while(m >= 128);
}
// parse route
if(msgHasRoute(type)) {
if(compressRoute) {
route = (bytes[offset++]) << 8 | bytes[offset++];
} else {
let routeLen = bytes[offset++];
if(routeLen) {
route = new ByteArray(routeLen);
copyArray(route, 0, bytes, offset, routeLen);
route = Protocol.strdecode(route);
} else {
route = '';
}
offset += routeLen;
}
}
// parse body
let bodyLen = bytesLen - offset;
let body = new ByteArray(bodyLen);
copyArray(body, 0, bytes, offset, bodyLen);
return {'id': id, 'type': type, 'compressRoute': compressRoute,
'route': route, 'body': body};
};
let copyArray = function(dest, doffset, src, soffset, length) {
if('function' === typeof src.copy) {
// Buffer
src.copy(dest, doffset, soffset, soffset + length);
} else {
// Uint8Array
for(let index=0; index
本文共计3956个文字,预计阅读时间需要16分钟。
Pomelo 客户端模块,包含:- 暴露 `Emitter` 对象- 初始化新的 `Emitter`- 公开 API:`Emitter` 构造函数
Pomelo客户端(function(){
/**
* Expose `Emitter`.
*/
// module.exports = Emitter;
/**
* Initialize a new `Emitter`.
*
* @api public
*/
function Emitter(obj) {
if (obj) return mixin(obj);
}
/**
* Mixin the emitter properties.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
function mixin(obj) {
for (let key in Emitter.prototype) {
obj[key] = Emitter.prototype[key];
}
return obj;
}
/**
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
(this._callbacks[event] = this._callbacks[event] || [])
.push(fn);
return this;
};
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.once = function(event, fn){
let self = this;
this._callbacks = this._callbacks || {};
function on() {
self.off(event, on);
fn.apply(this, arguments);
}
on.fn = fn;
this.on(event, on);
return this;
};
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
// all
if (0 === arguments.length) {
this._callbacks = {};
return this;
}
// specific event
let callbacks = this._callbacks[event];
if (!callbacks) return this;
// remove all handlers
if (1 === arguments.length) {
delete this._callbacks[event];
return this;
}
// remove specific handler
let cb;
for (let i = 0; i < callbacks.length; i++) {
cb = callbacks[i];
if (cb === fn || cb.fn === fn) {
callbacks.splice(i, 1);
break;
}
}
return this;
};
/**
* Emit `event` with the given args.
*
* @param {String} event
* @return {Emitter}
*/
Emitter.prototype.emit = function(event){
this._callbacks = this._callbacks || {};
let args = [].slice.call(arguments, 1)
, callbacks = this._callbacks[event];
if (callbacks) {
callbacks = callbacks.slice(0);
for (let i = 0, len = callbacks.length; i < len; ++i) {
callbacks[i].apply(this, args);
}
}
return this;
};
/**
* Return array of callbacks for `event`.
*
* @param {String} event
* @return {Array}
* @api public
*/
Emitter.prototype.listeners = function(event){
this._callbacks = this._callbacks || {};
return this._callbacks[event] || [];
};
/**
* Check if this emitter has `event` handlers.
*
* @param {String} event
* @return {Boolean}
* @api public
*/
Emitter.prototype.hasListeners = function(event){
return !! this.listeners(event).length;
};
if(typeof(window) !== "undefined") {
window.EventEmitter = Emitter;
}
})();
(function (exports, ByteArray, global) {
let Protocol = exports;
let PKG_HEAD_BYTES = 4;
let MSG_FLAG_BYTES = 1;
let MSG_ROUTE_CODE_BYTES = 2;
let MSG_ID_MAX_BYTES = 5;
let MSG_ROUTE_LEN_BYTES = 1;
let MSG_ROUTE_CODE_MAX = 0xffff;
let MSG_COMPRESS_ROUTE_MASK = 0x1;
let MSG_TYPE_MASK = 0x7;
let Package = Protocol.Package = {};
let Message = Protocol.Message = {};
Package.TYPE_HANDSHAKE = 1;
Package.TYPE_HANDSHAKE_ACK = 2;
Package.TYPE_HEARTBEAT = 3;
Package.TYPE_DATA = 4;
Package.TYPE_KICK = 5;
Message.TYPE_REQUEST = 0;
Message.TYPE_NOTIFY = 1;
Message.TYPE_RESPONSE = 2;
Message.TYPE_PUSH = 3;
/**
* pomele client encode
* id message id;
* route message route
* msg message body
* socketio current support string
*/
Protocol.strencode = function(str) {
let byteArray = new ByteArray(str.length * 3);
let offset = 0;
for(let i = 0; i < str.length; i++){
let charCode = str.charCodeAt(i);
let codes = null;
if(charCode <= 0x7f){
codes = [charCode];
}else if(charCode <= 0x7ff){
codes = [0xc0|(charCode>>6), 0x80|(charCode & 0x3f)];
}else{
codes = [0xe0|(charCode>>12), 0x80|((charCode & 0xfc0)>>6), 0x80|(charCode & 0x3f)];
}
for(let j = 0; j < codes.length; j++){
byteArray[offset] = codes[j];
++offset;
}
}
let _buffer = new ByteArray(offset);
copyArray(_buffer, 0, byteArray, 0, offset);
return _buffer;
};
/**
* client decode
* msg String data
* return Message Object
*/
Protocol.strdecode = function(buffer) {
let bytes = new ByteArray(buffer);
let array = [];
let offset = 0;
let charCode = 0;
let end = bytes.length;
while(offset < end){
if(bytes[offset] < 128){
charCode = bytes[offset];
offset += 1;
}else if(bytes[offset] < 224){
charCode = ((bytes[offset] & 0x3f)<<6) + (bytes[offset+1] & 0x3f);
offset += 2;
}else{
charCode = ((bytes[offset] & 0x0f)<<12) + ((bytes[offset+1] & 0x3f)<<6) + (bytes[offset+2] & 0x3f);
offset += 3;
}
array.push(charCode);
}
return String.fromCharCode.apply(null, array);
};
/**
* Package protocol encode.
*
* Pomelo package format:
* +------+-------------+------------------+
* | type | body length | body |
* +------+-------------+------------------+
*
* Head: 4bytes
* 0: package type,
* 1 - handshake,
* 2 - handshake ack,
* 3 - heartbeat,
* 4 - data
* 5 - kick
* 1 - 3: big-endian body length
* Body: body length bytes
*
* @param {Number} type package type
* @param {ByteArray} body body content in bytes
* @return {ByteArray} new byte array that contains encode result
*/
Package.encode = function(type, body){
let length = body ? body.length : 0;
let buffer = new ByteArray(PKG_HEAD_BYTES + length);
let index = 0;
buffer[index++] = type & 0xff;
buffer[index++] = (length >> 16) & 0xff;
buffer[index++] = (length >> 8) & 0xff;
buffer[index++] = length & 0xff;
if(body) {
copyArray(buffer, index, body, 0, length);
}
return buffer;
};
/**
* Package protocol decode.
* See encode for package format.
*
* @param {ByteArray} buffer byte array containing package content
* @return {Object} {type: package type, buffer: body byte array}
*/
Package.decode = function(buffer){
let offset = 0;
let bytes = new ByteArray(buffer);
let length = 0;
let rs = [];
while(offset < bytes.length) {
let type = bytes[offset++];
length = ((bytes[offset++]) << 16 | (bytes[offset++]) << 8 | bytes[offset++]) >>> 0;
let body = length ? new ByteArray(length) : null;
copyArray(body, 0, bytes, offset, length);
offset += length;
rs.push({'type': type, 'body': body});
}
return rs.length === 1 ? rs[0]: rs;
};
/**
* Message protocol encode.
*
* @param {Number} id message id
* @param {Number} type message type
* @param {Number} compressRoute whether compress route
* @param {Number|String} route route code or route string
* @param {Buffer} msg message body bytes
* @return {Buffer} encode result
*/
Message.encode = function(id, type, compressRoute, route, msg){
// caculate message max length
let idBytes = msgHasId(type) ? caculateMsgIdBytes(id) : 0;
let msgLen = MSG_FLAG_BYTES + idBytes;
if(msgHasRoute(type)) {
if(compressRoute) {
if(typeof route !== 'number'){
throw new Error('error flag for number route!');
}
msgLen += MSG_ROUTE_CODE_BYTES;
} else {
msgLen += MSG_ROUTE_LEN_BYTES;
if(route) {
route = Protocol.strencode(route);
if(route.length>255) {
throw new Error('route maxlength is overflow');
}
msgLen += route.length;
}
}
}
if(msg) {
msgLen += msg.length;
}
let buffer = new ByteArray(msgLen);
let offset = 0;
// add flag
offset = encodeMsgFlag(type, compressRoute, buffer, offset);
// add message id
if(msgHasId(type)) {
offset = encodeMsgId(id, buffer, offset);
}
// add route
if(msgHasRoute(type)) {
offset = encodeMsgRoute(compressRoute, route, buffer, offset);
}
// add body
if(msg) {
offset = encodeMsgBody(msg, buffer, offset);
}
return buffer;
};
/**
* Message protocol decode.
*
* @param {Buffer|Uint8Array} buffer message bytes
* @return {Object} message object
*/
Message.decode = function(buffer) {
let bytes = new ByteArray(buffer);
let bytesLen = bytes.length || bytes.byteLength;
let offset = 0;
let id = 0;
let route = null;
// parse flag
let flag = bytes[offset++];
let compressRoute = flag & MSG_COMPRESS_ROUTE_MASK;
let type = (flag >> 1) & MSG_TYPE_MASK;
// parse id
if(msgHasId(type)) {
let m = parseInt(bytes[offset]);
let i = 0;
do{
let m = parseInt(bytes[offset]);
id = id + ((m & 0x7f) * Math.pow(2,(7*i)));
offset++;
i++;
}while(m >= 128);
}
// parse route
if(msgHasRoute(type)) {
if(compressRoute) {
route = (bytes[offset++]) << 8 | bytes[offset++];
} else {
let routeLen = bytes[offset++];
if(routeLen) {
route = new ByteArray(routeLen);
copyArray(route, 0, bytes, offset, routeLen);
route = Protocol.strdecode(route);
} else {
route = '';
}
offset += routeLen;
}
}
// parse body
let bodyLen = bytesLen - offset;
let body = new ByteArray(bodyLen);
copyArray(body, 0, bytes, offset, bodyLen);
return {'id': id, 'type': type, 'compressRoute': compressRoute,
'route': route, 'body': body};
};
let copyArray = function(dest, doffset, src, soffset, length) {
if('function' === typeof src.copy) {
// Buffer
src.copy(dest, doffset, soffset, soffset + length);
} else {
// Uint8Array
for(let index=0; index

