-
-
Save Morabaraba/e0ec88c68d10a7c3684e5a1cdda6fc47 to your computer and use it in GitHub Desktop.
QICI Engine Message Queue Plugin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* mqclient awake the MQ Plugin. | |
* You atleast need one attached to a node. | |
*/ | |
var mqclient = qc.defineBehaviour('qc.engine.mqclient', qc.Behaviour, function() { | |
// need this behaviour be scheduled in editor | |
//this.runInEditor = true; | |
this.hostname = 'localhost'; | |
this.port = 8083; | |
this.path = '/mqtt'; | |
this.userName = 'guest'; | |
this.password = 'guest'; | |
this.rootTopic = 'mqgameengine/level1'; | |
this.singleton = true; | |
}, { | |
hostname: qc.Serializer.STRING, | |
port: qc.Serializer.INT, | |
userName: qc.Serializer.STRING, | |
password: qc.Serializer.STRING, | |
path: qc.Serializer.STRING, | |
rootTopic: qc.Serializer.STRING, | |
singleton: qc.Serializer.BOOLEAN, | |
}); | |
// Called when the script instance is being loaded. | |
mqclient.prototype.awake = function() { | |
if (this.singleton) { | |
// check in plugins | |
function findmq(plugin) { | |
return plugin instanceof PluginMQ; | |
} | |
var firstClient = this.game.plugins.plugins.find(findmq); | |
if (firstClient) { | |
this.plugin = firstClient; | |
return; | |
} | |
} | |
var mqclientConfig = this.game.storage.get('mqclientConfig'); | |
if (mqclientConfig) { | |
this.port = Number(mqclientConfig.port) || this.port; | |
this.hostname = mqclientConfig.hostname || this.hostname; | |
this.path = mqclientConfig.path || this.path; | |
this.rootTopic = mqclientConfig.rootTopic || this.rootTopic; | |
} | |
//change client id HACK TODO | |
this.clientId = 'clientId-Anon' + this.game.phaser.rnd.integer(); | |
// TODO can I have multiple plugins of the same constructor | |
this.plugin = this.game.plugins.add(PluginMQ, this); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* `PluginMQ` is a [QICI plugin](http://docs.qiciengine.com/api/plugin/index.html). | |
* You will not directly use this, instead add a `qc.engine.mqclient` to a node and use the `qc.engine.mqbehaviour`. | |
* If you add a `qc.engine.mqbehaviour` to a node remember to add `qc.engine.mqhandler` or your own behaviour to process events. | |
* | |
* @constructor | |
*/ | |
var PluginMQ = function(game, parent) {}; | |
PluginMQ.prototype.constructor = qc.NodeScheduler; | |
PluginMQ.prototype.utils = {}; | |
PluginMQ.prototype.init = function(behaviour) { | |
// console.log('init', arguments); | |
var self = this; | |
self.rootTopic = behaviour.rootTopic; | |
self.messages = []; | |
/** hash of other clients on the channel */ | |
self.clients = {}; | |
/* all the objects we want to sync */ | |
self.sync = {} | |
var client = new Paho.MQTT.Client( | |
behaviour.hostname, | |
behaviour.port, | |
behaviour.path, | |
behaviour.clientId); | |
self.client = client; | |
// hook our mq callbacks | |
// HACK to pass self/this to our callbacks instead of `client` as `this`, any better way TODO this? | |
client.onConnectionLost = function(responseObject) { | |
self.onConnectionLost.apply(self, [responseObject]); | |
}; | |
client.onMessageArrived = function(message) { | |
self.onMessageArrived.apply(self, [message]); | |
}; | |
var mqttConnectOptions = { | |
// hook up our chat onConnect and onFailure functions to our mqtt connection options | |
// HACK for `self` | |
onSuccess: function() { | |
self.onConnect.apply(self); | |
}, | |
onFailure: function() { | |
self.onFailure.apply(self); | |
}, | |
//useSSL: false, // atleast it is encrypted | |
//userName: this.userName, // I know this is insecure | |
//password: this.password, | |
keepAliveInterval: 30 // check every 30 sec if we are still allive | |
}; | |
client.connect(mqttConnectOptions); | |
self.mqttConnectOptions = mqttConnectOptions; | |
}; | |
PluginMQ.prototype.findNode = function(node) { | |
if (node.data.uniqueName && node.data.uniqueName[1]) { | |
uniqueName = node.data.uniqueName[1]; | |
//console.warn('finding uniqueName node ', uniqueName); | |
return this.game.nodePool.findByName(uniqueName); | |
} | |
else { | |
//console.warn('finding uuid node ', node.data.uuid); | |
return this.game.nodePool.find(node.data.uuid); | |
} | |
} | |
PluginMQ.prototype.preUpdate = function() { | |
var data = this.messages.pop(); | |
var game = this.game; | |
while (data) { | |
if (data.rpc) { | |
// this[data.rpc](data); # YOLO | |
switch (data.rpc) { | |
case 'registerClient': | |
this.registerClientEvent(data); | |
break; | |
case 'requestSyncAvail': | |
this.requestSyncAvailEvent(data); | |
break; | |
case 'requestSyncSync': | |
this.requestSyncSyncEvent(data); | |
break; | |
case 'requestSyncData': | |
this.requestSyncDataEvent(data); | |
break; | |
default: | |
console.error('Unknown rpc call', data.rpc); | |
} | |
// finished with rpc, lets do the next msg | |
data = this.messages.pop(); | |
continue; | |
} | |
var sprite = data.args[0]; | |
if (sprite.data) { | |
var e = data.args[1]; | |
var uuid = sprite.data.uuid; | |
var uniqueName; | |
var gameObj = this.findNode(sprite); | |
} | |
if (gameObj) { | |
var behaviour = gameObj.getScript('qc.engine.mqbehaviour'); | |
var eventBehaviour = gameObj.getScript(behaviour.eventBehaviour); | |
if (!eventBehaviour) { | |
console.error('Could not find eventBehaviour', behaviour.eventBehaviour, gameObj); | |
return; | |
} | |
eventBehaviour.preUpdateEvent(gameObj, sprite, e, data); | |
} | |
else { | |
console.error('gameObj not found ', data); | |
} | |
data = this.messages.pop(); | |
} | |
}; | |
PluginMQ.prototype.registerClient = function() { | |
this.send({rpc: 'registerClient', clientId: this.client.clientId}); | |
}; | |
PluginMQ.prototype.registerClientEvent = function(data) { | |
// if you scream in a open room your gonna hear echos | |
if (this.client.clientId === data.clientId) { return; }; | |
this.clients[data.clientId] = data; | |
// register ourself with the new client | |
// TODO this would be a good time to rebuild client list | |
this.registerClient(); | |
}; | |
//PluginMQ.prototype.deregisterClient = function() {}; | |
PluginMQ.prototype.requestSync = function() { | |
var uuid = this.game.math.uuid(); | |
this.requestSyncUuid = uuid; | |
this.send({rpc: 'requestSync', clientId: this.client.clientId, uuid: uuid}); | |
}; | |
PluginMQ.prototype.requestSyncEvent = function(data) { | |
if (this.client.clientId === data.clientId) { return; }; | |
this.send({rpc: 'requestSyncAvail', | |
toClientId: data.clientId, | |
clientId: this.client.clientId, | |
uuid: data.uuid, | |
}, {topic: this.rootTopic + '/' + data.clientId}); | |
}; | |
PluginMQ.prototype.requestSyncAvailEvent = function(data) { | |
if (this.client.clientId === data.clientId) { return; }; | |
if (this.waitingSync) return; | |
this.waitingSync = data.uuid; | |
this.send({rpc: 'requestSyncSync', clientId: this.client.clientId, uuid: data.uuid}, {topic: this.rootTopic + '/' + data.clientId}); | |
}; | |
PluginMQ.prototype.requestSyncSyncEvent = function(data) { | |
if (this.client.clientId === data.clientId) { return; }; | |
var list = []; | |
this.sync.forEach(function(node) { | |
var context = {}; | |
var json = self.game.serializer.buildBundle(arg, context); | |
json.dependences = self.game.serializer.combineDependence(context); | |
list.push(json); | |
}) | |
this.send({rpc: 'requestSyncData', sync: list}, {topic: this.rootTopic + '/' + data.clientId}); | |
}; | |
PluginMQ.prototype.utils.getObjNodePos = function(gameObject, node) { | |
var meta = this.gameObject.getMeta(); | |
// HACK a phaser object to get _anchoredX and _anchoredY from qici bundle data | |
var p = { | |
phaser: {}, | |
setWidth: function() {}, | |
setHeight: function() {}, | |
relayout: function() {} | |
}; | |
meta.position.set(p, node.data.position); | |
return p; | |
} | |
PluginMQ.prototype.requestSyncDataEvent = function(data) { | |
if (this.client.clientId === data.clientId) { return; }; | |
data.sync.forEach(function(json) { | |
var gameObj = this.findNode(json); | |
var p = this.utils.getObjNodePos(gameObj, json); | |
gameObj.x = p._anchoredX; | |
gameObj.y = p._anchoredY; | |
}); | |
}; | |
//PluginMQ.prototype.availSync = function() {}; | |
PluginMQ.prototype.send = function(payload, opts) { | |
opts = opts || {}; | |
var encoded = msgpack.encode(payload); | |
// console.debug('encoded length', encoded.length); | |
var message = new Paho.MQTT.Message(encoded); | |
message.qos = opts.qos || 0; | |
message.destinationName = opts.topic || this.rootTopic; | |
// console.debug('Sending Message', message); | |
// this.game.log.trace('Send Message to topic ' + message.destinationName + ' payload length ' + message.payloadBytes.length); | |
this.client.send(message); | |
} | |
// called on succesful connect | |
PluginMQ.prototype.onConnect = function() { | |
this.client.subscribe(this.rootTopic + '/' + this.client.clientId); | |
this.client.subscribe(this.rootTopic); | |
console.debug('Connected, register and syncing'); | |
this.registerClient(); | |
this.requestSync(); | |
} | |
// called when client connect fail | |
PluginMQ.prototype.onFailure = function() { | |
console.error('Connection Failed', arguments, this); | |
} | |
/** @memberof! chat# */ | |
// called when the client loses its connection | |
PluginMQ.prototype.onConnectionLost = function(responseObject) { | |
delete this.mqttConnectOptions.mqttVersion; | |
delete this.mqttConnectOptions.mqttVersionExplicit; | |
// console.error('onConnectionLost', responseObject, this, this.mqttConnectOptions); | |
this.client.connect(this.mqttConnectOptions); | |
} | |
PluginMQ.prototype.onMessageArrived = function(message) { | |
var data = msgpack.decode(message.payloadBytes); | |
// console.debug('onMessageArrived decoded payload', data); | |
this.messages.push(data); | |
} | |
/* | |
PluginMQ.prototype.update = function() { | |
console.log('update'); | |
}; | |
PluginMQ.prototype.postUpdate = function() { | |
console.log('postUpdate'); | |
}; | |
PluginMQ.prototype.render = function() { | |
console.log('render'); | |
}; | |
PluginMQ.prototype.postRender = function() { | |
console.log('postRender'); | |
}; | |
PluginMQ.prototype.destroy = function() { | |
console.log('destroy'); | |
}; | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment