From 4a4c1049089ab5c46e36d2747339f1fd3d137ae2 Mon Sep 17 00:00:00 2001 From: Darren Date: Tue, 11 Dec 2012 22:20:05 +0000 Subject: [PATCH] Server: Revised module system --- server/modules.js | 10 +- server/plugininterface.js | 212 ++++++++++++++++++++++++++++++++++++++ server_modules/example.js | 4 +- 3 files changed, 219 insertions(+), 7 deletions(-) create mode 100644 server/plugininterface.js diff --git a/server/modules.js b/server/modules.js index aed7743..0553974 100644 --- a/server/modules.js +++ b/server/modules.js @@ -1,6 +1,7 @@ var events = require('events'), util = require('util'), - _ = require('lodash'); + _ = require('lodash'), + EventPublisher = require('./plugininterface.js'); /** @@ -14,9 +15,7 @@ var active_publisher; // Create a publisher to allow event subscribing function Publisher (obj) { - var EventPublisher = function modulePublisher() {}; - util.inherits(EventPublisher, events.EventEmitter); - + var EventPublisher = require('./plugininterface.js'); return new EventPublisher(); } @@ -47,6 +46,7 @@ function loadModule (module_file) { delete require.cache[require.resolve(module_file)]; } catch (err) { // Module was not found + console.log(err); return false; } @@ -90,7 +90,7 @@ Module.prototype.on = function (event_name, fn) { this._events[event_name].push(fn); // If this is an internal event, do not propogate the event - if (internal_events.indexOf(event_name) !== -1) { + if (internal_events.indexOf(event_name) === -1) { active_publisher.on(event_name, fn); } }; diff --git a/server/plugininterface.js b/server/plugininterface.js new file mode 100644 index 0000000..5566e54 --- /dev/null +++ b/server/plugininterface.js @@ -0,0 +1,212 @@ +/* + * The same functionality as EventEmitter but with the inclusion of callbacks + */ + +/* + * Promise style object to emit events to listeners + */ +function EmitCall (event_name, event_data) { + var that = this, + completed = false, + completed_fn = []; + + + // Emit this event to an array of listeners + function callListeners(listeners) { + var current_event_idx = -1; + + // Make sure we have some data to pass to the listeners + event_data = event_data || undefined; + + // If no bound listeners for this event, leave now + if (listeners.length === 0) { + emitComplete(); + return; + } + + + // Call the next listener in our array + function nextListener() { + var listener, event_obj; + + // We want the next listener + current_event_idx++; + + // If we've ran out of listeners end this emit call + if (!listeners[current_event_idx]) { + emitComplete(); + return; + } + + // Object the listener ammends to tell us what it's going to do + event_obj = { + // If changed to true, expect this listener is going to callback + wait: false, + + // If wait is true, this callback must be called to continue running listeners + callback: function () { + // Invalidate this callback incase a listener decides to call it again + callback = undefined; + + nextListener.apply(that); + } + }; + + + listener = listeners[current_event_idx]; + listener[1].call(listener[2] || that, event_obj, event_data); + + // If the listener hasn't signalled it's going to wait, proceed to next listener + if (!event_obj.wait) { + // Invalidate the callback just incase a listener decides to call it anyway + event_obj.callback = undefined; + + nextListener(); + } + } + + nextListener(); + } + + + + function emitComplete() { + completed = true; + + // Call the completed functions + (completed_fn || []).forEach(function (fn) { + if (typeof fn === 'function') fn(); + }); + } + + + + function done(fn) { + // Only accept functions + if (typeof fn !== 'function') return false; + + completed_fn.push(fn); + + // If we have already completed the emits, call this now + if (completed) fn(); + } + + + return { + callListeners: callListeners, + done: done + }; +} + + + + + + +function PluginInterface () { +} + + +// Holder for all the bound listeners by this module +PluginInterface.prototype._listeners = {}; + + + +PluginInterface.prototype.on = function (event_name, fn, scope) { + this._listeners[event_name] = this._listeners[event_name] || []; + this._listeners[event_name].push(['on', fn, scope]); +}; + + + +PluginInterface.prototype.once = function (event_name, fn, scope) { + this._listeners[event_name] = this._listeners[event_name] || []; + this._listeners[event_name].push(['once', fn, scope]); +}; + + + +PluginInterface.prototype.off = function (event_name, fn, scope) { + var idx; + + if (typeof event_name === 'undefined') { + // Remove all listeners + this._listeners = []; + + } else if (typeof fn === 'undefined') { + // Remove all of 1 event type + delete this._listeners[event_name]; + + } else if (typeof scope === 'undefined') { + // Remove a single event type + callback + for (idx in (this._listeners[event_name] || [])) { + if (this._listeners[event_name][idx][1] === fn) { + delete this._listeners[event_name][idx]; + } + } + } else { + // Remove a single event type + callback + scope + for (idx in (this._listeners[event_name] || [])) { + if (this._listeners[event_name][idx][1] === fn && this._listeners[event_name][idx][2] === scope) { + delete this._listeners[event_name][idx]; + } + } + } +}; + + + +// Call all the listeners for a certain event, passing them some event data that may be changed +PluginInterface.prototype.emit = function (event_name, event_data) { + var emitter = new EmitCall(event_name, event_data); + var listeners = this._listeners[event_name] || []; + + // Once emitted, remove any 'once' bound listeners + emitter.done(function () { + listeners.forEach(function (listener, idx) { + if (listener[0] === 'once') { + listeners[idx] = undefined; + } + }); + }); + + // Emit the event to the listeners and return + emitter.callListeners(listeners); + return emitter; +}; + + +module.exports = PluginInterface; + + + +/* + * Example usage + */ + + +/* +var modules = new PluginInterface(); + + + +// A plugin +modules.on('client:command', function (event, data) { + //event.wait = true; + setTimeout(event.callback, 2000); +}); + + + + +// Core code that is being extended by plugins +var data = { + nick: 'prawnsalald', + command: '/dothis' +}; + + +modules.emit('client:command', data).done(function () { + console.log('Your command is: ' + data.command); +}); +*/ \ No newline at end of file diff --git a/server_modules/example.js b/server_modules/example.js index df11360..2d33755 100644 --- a/server_modules/example.js +++ b/server_modules/example.js @@ -3,12 +3,12 @@ var kiwiModules = require('../server/modules'); var module = new kiwiModules.Module('Example Module'); -module.subscribe('client:connected', function(data) { +module.on('client:connected', function(event, data) { console.log('Client connection:', data); }); -module.subscribe('client:commands:msg', function(data) { +module.on('client:commands:msg', function(event, data) { console.log('Client msg:', data.args.target, ': ', data.args.msg); data.args.msg += ' - modified!'; }); \ No newline at end of file -- 2.25.1