From 1ec38cc2390c318a2789cc6eaca4e816fd024857 Mon Sep 17 00:00:00 2001 From: pandamicro Date: Mon, 23 Jul 2018 17:25:54 +0800 Subject: [PATCH] Fix beta user reported issues (#3004) * Fix doc, error & cleanup code * Fix render component nested in mask issue * Fix multiple spine textures rendering issue * Box2d stability * Upgrade renderer.js to 1.8.6 * Avoid particle default values override custom issue * Fix ParticleSystem restore issue in editor * Fix enumerate scene.active issue * Fix error map with new ids --- EngineErrorMap.md | 70 +++++++++++++++++++ cocos2d/actions/CCAction.js | 12 ++-- cocos2d/core/CCNode.js | 2 +- cocos2d/core/assets/CCSpriteFrame.js | 4 +- cocos2d/core/event-manager/CCEventListener.js | 4 +- cocos2d/core/event-manager/CCEventManager.js | 8 +-- cocos2d/core/load-pipeline/text-downloader.js | 7 +- cocos2d/core/physics/CCRigidBody.js | 2 +- cocos2d/core/platform/CCMacro.js | 4 +- cocos2d/core/renderer/render-engine.js | 7 +- .../core/renderer/webgl/stencil-manager.js | 1 + cocos2d/deprecated.js | 2 +- cocos2d/particle/CCParticleSystem.js | 20 +++--- cocos2d/particle/particle-simulator.js | 2 +- cocos2d/tilemap/CCTiledLayer.js | 20 +++--- cocos2d/tilemap/CCTiledMap.js | 2 +- cocos2d/webview/webview-impl.js | 4 +- extensions/spine/spine-assembler.js | 7 +- external/box2d/box2d.js | 4 +- 19 files changed, 130 insertions(+), 52 deletions(-) diff --git a/EngineErrorMap.md b/EngineErrorMap.md index 0759dfffc89..f1314fb05f3 100644 --- a/EngineErrorMap.md +++ b/EngineErrorMap.md @@ -396,6 +396,7 @@ cocos2d: Could not initialize cc.AtlasNode. Invalid Texture. ### 1622 + _ccsg.Node._requestDirtyFlag: failed to satisfy the request, key (%s) for flag have already been taken ### 1623 @@ -420,18 +421,22 @@ Not support for asynchronous creating node in SG ### 1628 + Renderer error: Size of the cc._RendererInSG._sgNode must be zero ### 1629 + The node '%s' has a component inherited from 'cc._RendererInSG' ### 1630 + JSB environment is not support invoke node.runAction before the 'cc._RendererInSG' component enabled. ### 1631 + Please use runAction in the method 'start' instead. ### 1632 @@ -1558,6 +1563,7 @@ _ccsg.Label._initBMFontWithString(): Impossible to create font. Please check fil ### 4002 + _ccsg.Label._initBMFontWithString(): re-init is no longer supported ### 4003 @@ -2008,26 +2014,32 @@ cc.ParticleBatchNode._addChildHelper(): child already added. It can't be added a ### 6008 + _ccsg.ParticleSystem.initWithFile(): Particles: file not found ### 6009 + _ccsg.ParticleSystem.initWithDictionary(): Invalid emitterType in config file ### 6010 + _ccsg.ParticleSystem: error decoding or ungzipping textureImageData ### 6011 + _ccsg.ParticleSystem: unknown image format with Data ### 6012 + _ccsg.ParticleSystem.initWithDictionary() : error loading the texture ### 6013 + Particle system: not enough memory ### 6014 @@ -2037,6 +2049,7 @@ Can't change blending functions when the particle is being batched ### 6015 + _ccsg.ParticleSystem.setDisplayFrame(): QuadParticle only supports SpriteFrames with no offsets ### 6016 @@ -2096,6 +2109,18 @@ Unknown Photometric Interpretation: %s Unkown error +### 6030 + +cc.ParticleSystem: error decoding or ungzipping textureImageData + +### 6031 + +cc.ParticleSystem: unknown image format with Data + +### 6032 + +cc.ParticleSystem.initWithDictionary() : error loading the texture + ### 6100 @@ -2239,26 +2264,32 @@ Property 'mapLoaded' is unused now. Please write the logic to the callback 'star ### 7204 + _ccsg.TMXLayer.getTileAt(): TMXLayer: the tiles map has been released ### 7205 + _ccsg.TMXLayer.getTileGIDAt(): TMXLayer: the tiles map has been released ### 7206 + _ccsg.TMXLayer.setTileGID(): TMXLayer: the tiles map has been released ### 7207 + _ccsg.TMXLayer.setTileGID(): invalid gid: %s ### 7208 + _ccsg.TMXLayer.getTileFlagsAt(): TMXLayer: the tiles map has been released ### 7209 + _ccsg.TMXLayer.removeTileAt(): TMXLayer: the tiles map has been released ### 7210 @@ -2271,10 +2302,12 @@ TMX invalid value ### 7212 + _ccsg.TMXTiledMap.initWithTMXFile(): Map not found. Please check the filename. ### 7213 + _ccsg.TMXTiledMap.initWithXML(): Map not found. Please check the filename. ### 7214 @@ -2316,56 +2349,93 @@ Parse %s failed. ### 7223 + _ccsg.TMXLayer.setTileGID(): pos should be non-null ### 7224 + _ccsg.TMXTiledMap.getLayer(): layerName should be non-null or non-empty string. ### 7225 + _ccsg.TMXTiledMap.getObjectGroup(): groupName should be non-null or non-empty string. ### 7226 + _ccsg.TMXLayer.getTileAt(): pos should be non-null ### 7227 + _ccsg.TMXLayer.getTileAt(): invalid position ### 7228 + _ccsg.TMXLayer.getTileGIDAt(): pos should be non-null ### 7229 + _ccsg.TMXLayer.getTileGIDAt(): invalid position ### 7230 + _ccsg.TMXLayer.setTileGID(): pos should be non-null ### 7231 + _ccsg.TMXLayer.setTileGID(): invalid position ### 7232 + _ccsg.TMXLayer.getTileFlagsAt(): pos should be non-null ### 7233 + _ccsg.TMXLayer.getTileFlagsAt(): invalid position ### 7234 + _ccsg.TMXLayer.removeTileAt(): pos should be non-null ### 7235 + _ccsg.TMXLayer.removeTileAt(): invalid position +### 7236 + +cc.TMXLayer.getTileAt(): TMXLayer: the tiles map has been released + +### 7237 + +cc.TMXLayer.getTileGIDAt(): TMXLayer: the tiles map has been released + +### 7238 + +cc.TMXLayer.setTileGID(): TMXLayer: the tiles map has been released + +### 7239 + +cc.TMXLayer.setTileGID(): invalid gid: %s + +### 7240 + +cc.TMXLayer.getTileFlagsAt(): TMXLayer: the tiles map has been released + +### 7241 + +cc.TiledMap.initWithXML(): Map not found. Please check the filename. + ### 7300 diff --git a/cocos2d/actions/CCAction.js b/cocos2d/actions/CCAction.js index a856fb067be..4d98beb3a83 100644 --- a/cocos2d/actions/CCAction.js +++ b/cocos2d/actions/CCAction.js @@ -400,17 +400,15 @@ cc.speed = function (action, speed) { * @property {Number} topBoundary - world topBoundary. * @property {Number} bottomBoundary - world bottomBoundary. * - * @param {_ccsg.Node} followedNode + * @param {cc.Node} followedNode * @param {Rect} rect * @example * // creates the action with a set boundary - * var sprite = new _ccsg.Sprite("spriteFileName"); - * var followAction = new cc.Follow(sprite, cc.rect(0, 0, s.width * 2 - 100, s.height)); + * var followAction = new cc.Follow(node, cc.rect(0, 0, s.width * 2 - 100, s.height)); * this.runAction(followAction); * * // creates the action with no boundary set - * var sprite = new _ccsg.Sprite("spriteFileName"); - * var followAction = new cc.Follow(sprite); + * var followAction = new cc.Follow(node); * this.runAction(followAction); * * @class @@ -424,7 +422,7 @@ cc.Follow = cc.Class({ * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function.
* creates the action with a set boundary.
* creates the action with no boundary set. - * @param {_ccsg.Node} followedNode + * @param {cc.Node} followedNode * @param {Rect} rect */ ctor:function (followedNode, rect) { @@ -478,7 +476,7 @@ cc.Follow = cc.Class({ /* * initializes the action with a set boundary. * - * @param {_ccsg.Node} followedNode + * @param {cc.Node} followedNode * @param {Rect} [rect=] * @return {Boolean} */ diff --git a/cocos2d/core/CCNode.js b/cocos2d/core/CCNode.js index 0ce956534b7..fee8abf7e0a 100644 --- a/cocos2d/core/CCNode.js +++ b/cocos2d/core/CCNode.js @@ -1097,7 +1097,7 @@ var Node = cc.Class({ * @method constructor * @param {String} [name] */ - ctor (name) { + ctor () { this._reorderChildDirty = false; // cache component diff --git a/cocos2d/core/assets/CCSpriteFrame.js b/cocos2d/core/assets/CCSpriteFrame.js index 91ed35c7f84..e68e0ff075e 100644 --- a/cocos2d/core/assets/CCSpriteFrame.js +++ b/cocos2d/core/assets/CCSpriteFrame.js @@ -33,12 +33,12 @@ let temp_uvs = [{u: 0, v: 0}, {u: 0, v: 0}, {u: 0, v: 0}, {u: 0, v: 0}]; /** * !#en * A cc.SpriteFrame has:
- * - texture: A cc.Texture2D that will be used by the _ccsg.Sprite
+ * - texture: A cc.Texture2D that will be used by render components
* - rectangle: A rectangle of the texture * * !#zh * 一个 SpriteFrame 包含:
- * - 纹理:会被 Sprite 使用的 Texture2D 对象。
+ * - 纹理:会被渲染组件使用的 Texture2D 对象。
* - 矩形:在纹理中的矩形区域。 * * @class SpriteFrame diff --git a/cocos2d/core/event-manager/CCEventListener.js b/cocos2d/core/event-manager/CCEventListener.js index 00bd646b35f..cd1ba37b1c8 100644 --- a/cocos2d/core/event-manager/CCEventListener.js +++ b/cocos2d/core/event-manager/CCEventListener.js @@ -149,7 +149,7 @@ cc.EventListener.prototype = { /* * Sets scene graph priority for this listener - * @param {_ccsg.Node|Node} node + * @param {cc.Node} node * @private */ _setSceneGraphPriority: function (node) { @@ -159,7 +159,7 @@ cc.EventListener.prototype = { /* * Gets scene graph priority of this listener - * @returns {_ccsg.Node|cc.Node} if it's a fixed priority listener, non-null for scene graph priority listener + * @returns {cc.Node} if it's a fixed priority listener, non-null for scene graph priority listener * @private */ _getSceneGraphPriority: function () { diff --git a/cocos2d/core/event-manager/CCEventManager.js b/cocos2d/core/event-manager/CCEventManager.js index 501e3896df9..f7ee2fc2f79 100644 --- a/cocos2d/core/event-manager/CCEventManager.js +++ b/cocos2d/core/event-manager/CCEventManager.js @@ -146,7 +146,7 @@ var eventManager = { * @param {Boolean} [recursive=false] */ pauseTarget: function (node, recursive) { - if (!(node instanceof cc._BaseNode || node instanceof _ccsg.Node)) { + if (!(node instanceof cc._BaseNode)) { cc.warnID(3506); return; } @@ -170,7 +170,7 @@ var eventManager = { * @param {Boolean} [recursive=false] */ resumeTarget: function (node, recursive) { - if (!(node instanceof cc._BaseNode || node instanceof _ccsg.Node)) { + if (!(node instanceof cc._BaseNode)) { cc.warnID(3506); return; } @@ -732,7 +732,7 @@ var eventManager = { */ addListener: function (listener, nodeOrPriority) { cc.assertID(listener && nodeOrPriority, 3503); - if (!(cc.js.isNumber(nodeOrPriority) || nodeOrPriority instanceof cc._BaseNode || nodeOrPriority instanceof _ccsg.Node)) { + if (!(cc.js.isNumber(nodeOrPriority) || nodeOrPriority instanceof cc._BaseNode)) { cc.warnID(3506); return; } @@ -902,7 +902,7 @@ var eventManager = { */ removeListeners: function (listenerType, recursive) { var i, _t = this; - if (!(cc.js.isNumber(listenerType) || listenerType instanceof cc._BaseNode || listenerType instanceof _ccsg.Node)) { + if (!(cc.js.isNumber(listenerType) || listenerType instanceof cc._BaseNode)) { cc.warnID(3506); return; } diff --git a/cocos2d/core/load-pipeline/text-downloader.js b/cocos2d/core/load-pipeline/text-downloader.js index 79af076cbc4..deb9970f355 100644 --- a/cocos2d/core/load-pipeline/text-downloader.js +++ b/cocos2d/core/load-pipeline/text-downloader.js @@ -23,10 +23,8 @@ THE SOFTWARE. ****************************************************************************/ -var sys = require('../platform/CCSys'); - if (CC_JSB) { - module.exports = function (item, callback) { + module.exports = function (item) { var url = item.url; var result = jsb.fileUtils.getStringFromFile(url); @@ -46,8 +44,7 @@ else { url = urlAppendTimestamp(url); var xhr = cc.loader.getXMLHttpRequest(), - errInfo = 'Load text file failed: ' + url, - navigator = window.navigator; + errInfo = 'Load text file failed: ' + url; xhr.open('GET', url, true); if (xhr.overrideMimeType) xhr.overrideMimeType('text\/plain; charset=utf-8'); xhr.onload = function () { diff --git a/cocos2d/core/physics/CCRigidBody.js b/cocos2d/core/physics/CCRigidBody.js index de78ea72934..da71792b99e 100644 --- a/cocos2d/core/physics/CCRigidBody.js +++ b/cocos2d/core/physics/CCRigidBody.js @@ -65,7 +65,7 @@ var RigidBody = cc.Class({ return this._enabled; }, set: function () { - cc.warnID('8200'); + cc.warnID(8200); }, visible: false, override: true diff --git a/cocos2d/core/platform/CCMacro.js b/cocos2d/core/platform/CCMacro.js index b5c32dd7942..55121b8fbda 100644 --- a/cocos2d/core/platform/CCMacro.js +++ b/cocos2d/core/platform/CCMacro.js @@ -192,8 +192,8 @@ cc.macro = { * This formula prevents artifacts by using 99% of the texture.
* The "correct" way to prevent artifacts is by expand the texture's border with the same color by 1 pixel
*
- * Affected nodes:
- * - _ccsg.TMXLayer
+ * Affected component:
+ * - cc.TMXLayer
*
* Enabled by default. To disabled set it to 0.
* To modify it, in Web engine please refer to CCMacro.js, in JSB please refer to CCConfig.h diff --git a/cocos2d/core/renderer/render-engine.js b/cocos2d/core/renderer/render-engine.js index 7da0403436e..2a83954a7aa 100644 --- a/cocos2d/core/renderer/render-engine.js +++ b/cocos2d/core/renderer/render-engine.js @@ -13389,8 +13389,11 @@ Base.prototype._draw = function _draw (item) { node.getWorldMatrix(_m4_tmp$2); device.setUniform('model', mat4.array(_float16_pool.add(), _m4_tmp$2)); - mat3.transpose(_m3_tmp$1, mat3.invert(_m3_tmp$1, mat3.fromMat4(_m3_tmp$1, _m4_tmp$2))); - device.setUniform('normalMatrix', mat3.array(_float9_pool.add(), _m3_tmp$1)); + var inverse = mat3.invert(_m3_tmp$1, mat3.fromMat4(_m3_tmp$1, _m4_tmp$2)); + if (inverse) { + mat3.transpose(_m3_tmp$1, inverse); + device.setUniform('normalMatrix', mat3.array(_float9_pool.add(), _m3_tmp$1)); + } // } // set technique uniforms diff --git a/cocos2d/core/renderer/webgl/stencil-manager.js b/cocos2d/core/renderer/webgl/stencil-manager.js index d5c259305b3..5e0c41776c2 100644 --- a/cocos2d/core/renderer/webgl/stencil-manager.js +++ b/cocos2d/core/renderer/webgl/stencil-manager.js @@ -119,6 +119,7 @@ StencilManager.prototype = { pass.setStencilFront(func, ref, stencilMask, failOp, zFailOp, zPassOp, writeMask); pass.setStencilBack(func, ref, stencilMask, failOp, zFailOp, zPassOp, writeMask); } + return effect; }, pushMask (mask) { diff --git a/cocos2d/deprecated.js b/cocos2d/deprecated.js index c31dbe7b675..dded7ff5c88 100644 --- a/cocos2d/deprecated.js +++ b/cocos2d/deprecated.js @@ -366,7 +366,7 @@ if (CC_DEV) { // SCENE var ERR = '"%s" is not defined in the Scene, it is only defined in normal nodes.'; - Object.defineProperties(cc.Scene.prototype, { + CC_EDITOR || Object.defineProperties(cc.Scene.prototype, { active: { get: function () { cc.error(ERR, 'active'); diff --git a/cocos2d/particle/CCParticleSystem.js b/cocos2d/particle/CCParticleSystem.js index 444c071d69a..0522bb7e386 100644 --- a/cocos2d/particle/CCParticleSystem.js +++ b/cocos2d/particle/CCParticleSystem.js @@ -813,7 +813,9 @@ var ParticleSystem = cc.Class({ }, onLoad () { - ParticleSystem._assembler.createIA(this); + if (!this._ia) { + ParticleSystem._assembler.createIA(this); + } }, onEnable () { @@ -921,17 +923,17 @@ var ParticleSystem = cc.Class({ } self._plistFile = file.nativeUrl; + if (!self._custom) { + self._initWithDictionary(content); + } if (!self.spriteFrame) { if (file.texture) { self.spriteFrame = new cc.SpriteFrame(file.texture); } - else { + else if (self._custom) { self._initTextureWithDictionary(content); } } - if (!self._custom) { - self._initWithDictionary(content); - } }); } }, @@ -950,13 +952,13 @@ var ParticleSystem = cc.Class({ if (textureData && textureData.length > 0) { var buffer = codec.unzipBase64AsArray(textureData, 1); if (!buffer) { - cc.logID(6010); + cc.logID(6030); return false; } var imageFormat = getImageFormatByData(buffer); if (imageFormat !== macro.ImageFormat.TIFF && imageFormat !== macro.ImageFormat.PNG) { - cc.logID(6011); + cc.logID(6031); return false; } @@ -970,7 +972,7 @@ var ParticleSystem = cc.Class({ var tex = textureUtil.cacheImage(imgPath, canvasObj); if (!tex) - cc.logID(6012); + cc.logID(6032); // TODO: Use cc.loader to load asynchronously the SpriteFrame object, avoid using textureUtil this.spriteFrame = new cc.SpriteFrame(tex); } @@ -1093,7 +1095,7 @@ var ParticleSystem = cc.Class({ return true; }, - _onTextureLoaded: function (event) { + _onTextureLoaded: function () { this._texture = this._spriteFrame.getTexture(); this._simulator.updateUVs(true); // Reactivate material diff --git a/cocos2d/particle/particle-simulator.js b/cocos2d/particle/particle-simulator.js index 4e26cf1a45d..d27816e04d6 100644 --- a/cocos2d/particle/particle-simulator.js +++ b/cocos2d/particle/particle-simulator.js @@ -301,7 +301,7 @@ Simulator.prototype.step = function (dt) { let buffer = psys._buffer; let particleCount = particles.length; buffer.reset(); - buffer.request(particleCount * 4, particles.length * 6); + buffer.request(particleCount * 4, particleCount * 6); // Fill up uvs if (particleCount > this._uvFilled) { diff --git a/cocos2d/tilemap/CCTiledLayer.js b/cocos2d/tilemap/CCTiledLayer.js index ddd8f199cc9..04ccd2f8fdf 100644 --- a/cocos2d/tilemap/CCTiledLayer.js +++ b/cocos2d/tilemap/CCTiledLayer.js @@ -205,7 +205,7 @@ let TiledLayer = cc.Class({ */ setTileGIDAt (gid, posOrX, flagsOrY, flags) { if (posOrX === undefined) { - throw new Error("_ccsg.TMXLayer.setTileGID(): pos should be non-null"); + throw new Error("cc.TiledLayer.setTileGIDAt(): pos should be non-null"); } let pos; if (flags !== undefined || !(posOrX instanceof cc.Vec2)) { @@ -219,14 +219,14 @@ let TiledLayer = cc.Class({ pos.x = Math.floor(pos.x); pos.y = Math.floor(pos.y); if (this._isInvalidPosition(pos)) { - throw new Error("CCTiledLayer.setTileGID(): invalid position"); + throw new Error("cc.TiledLayer.setTileGIDAt(): invalid position"); } if (!this._tiles) { - cc.logID(7206); + cc.logID(7238); return; } if (gid !== 0 && gid < this._tileset.firstGid) { - cc.logID(7207, gid); + cc.logID(7239, gid); return; } @@ -267,7 +267,7 @@ let TiledLayer = cc.Class({ */ getTileGIDAt (pos, y) { if (pos === undefined) { - throw new Error("_ccsg.TMXLayer.getTileGIDAt(): pos should be non-null"); + throw new Error("cc.TiledLayer.getTileGIDAt(): pos should be non-null"); } let x = pos; if (y === undefined) { @@ -275,10 +275,10 @@ let TiledLayer = cc.Class({ y = pos.y; } if (this._isInvalidPosition(x, y)) { - throw new Error("_ccsg.TMXLayer.getTileGIDAt(): invalid position"); + throw new Error("cc.TiledLayer.getTileGIDAt(): invalid position"); } if (!this._tiles) { - cc.logID(7205); + cc.logID(7237); return null; } @@ -300,7 +300,7 @@ let TiledLayer = cc.Class({ throw new Error("TiledLayer.getTileFlagsAt: invalid position"); } if (!this._tiles) { - cc.logID(7208); + cc.logID(7240); return null; } @@ -335,7 +335,7 @@ let TiledLayer = cc.Class({ throw new Error("TiledLayer.getTiledTileAt: invalid position"); } if (!this._tiles) { - cc.logID(7204); + cc.logID(7236); return null; } @@ -370,7 +370,7 @@ let TiledLayer = cc.Class({ throw new Error("TiledLayer.setTiledTileAt: invalid position"); } if (!this._tiles) { - cc.logID(7204); + cc.logID(7236); return null; } diff --git a/cocos2d/tilemap/CCTiledMap.js b/cocos2d/tilemap/CCTiledMap.js index 538465fce29..d78edc6c60e 100644 --- a/cocos2d/tilemap/CCTiledMap.js +++ b/cocos2d/tilemap/CCTiledMap.js @@ -459,7 +459,7 @@ let TiledMap = cc.Class({ let mapInfo = new cc.TMXMapInfo(file.tmxXmlStr, tsxMap, textures); let tilesets = mapInfo.getTilesets(); if(!tilesets || tilesets.length === 0) - cc.logID(7213); + cc.logID(7241); this._buildWithMapInfo(mapInfo); } diff --git a/cocos2d/webview/webview-impl.js b/cocos2d/webview/webview-impl.js index 8b0f36934e0..daaedb1d2e9 100644 --- a/cocos2d/webview/webview-impl.js +++ b/cocos2d/webview/webview-impl.js @@ -296,7 +296,7 @@ let WebViewImpl = cc.Class({ /** * The binding event - * @param {_ccsg.WebView.EventType} event + * @param {WebViewImpl.EventType} event * @param {Function} callback */ setEventListener (event, callback) { @@ -305,7 +305,7 @@ let WebViewImpl = cc.Class({ /** * Delete events - * @param {_ccsg.WebView.EventType} event + * @param {WebViewImpl.EventType} event */ removeEventListener (event) { this._EventList[event] = null; diff --git a/extensions/spine/spine-assembler.js b/extensions/spine/spine-assembler.js index a0b7091b432..15a5c44e400 100644 --- a/extensions/spine/spine-assembler.js +++ b/extensions/spine/spine-assembler.js @@ -195,8 +195,13 @@ var spineAssembler = { if (currMaterial !== material) { if (currMaterial) { newData = true; + data.material = currMaterial; } - data.material = currMaterial = material; + else { + // Init data material + data.material = material; + } + currMaterial = material; } // Request new render data and new vertex content diff --git a/external/box2d/box2d.js b/external/box2d/box2d.js index b840d670d11..7f31032d44e 100644 --- a/external/box2d/box2d.js +++ b/external/box2d/box2d.js @@ -3614,7 +3614,9 @@ var primaryPair = this.m_pairBuffer[i]; var userDataA = primaryPair.proxyA.userData; // this.m_tree.GetUserData(primaryPair.proxyA); var userDataB = primaryPair.proxyB.userData; // this.m_tree.GetUserData(primaryPair.proxyB); - callback(userDataA, userDataB); + if (userDataA && userDataB) { + callback(userDataA, userDataB); + } ++i; // Skip any duplicate pairs. while (i < this.m_pairCount) {