Permalink
Browse files

Change to support callbacks and multiple responses per packet received.

* Changed allmost all methods to use callbacks instead of returning data.
  This fits better with the async nature of node. This use a queue of request that will timeout if
  no response is received.
* Reworked the readPacket method to support more than one response per packet.
* Rename in packet.json to rcon instead of node-rcon as per the npm documentation
  http://npmjs.org/doc/developers.html
  • Loading branch information...
1 parent 2a93a5b commit 3c685fba8be38259c1943038052ab2e63cdf6acf @kmpm kmpm committed Nov 19, 2011
Showing with 142 additions and 29 deletions.
  1. +6 −0 README.md
  2. +25 −0 examples/minecraft.js
  3. +103 −26 node-rcon.js
  4. +8 −3 package.json
View
@@ -0,0 +1,6 @@
+# Node RCON #
+A RCON protocol wrapper for Node
+
+## Example of usage ##
+It the examples folder you will find a small app that connects to the rcon port of a [Minecraft](http://www.minecraft.net) server.
+
View
@@ -0,0 +1,25 @@
+
+var Rcon = require('../node-rcon').newHandle;
+var rcon = new Rcon();
+
+rcon.connect("localhost", 25575, "supersecret", onConnected);
+
+
+function onConnected(err, response){
+ if(err){console.error(err);return;}
+
+ console.log("connected", response);
+
+ //make superman a op
+ var rc = rcon.sendCommand("op superman", function(err, response){
+ console.log("result of op:", err, response);
+ });
+
+ //revoke his newly found status
+ rcon.sendCommand("deop superman", function(err, response){
+ console.log("result of deop:", err, response);
+ });
+
+ rcon.end();
+}
+
View
@@ -1,5 +1,10 @@
var net = require('net');
+var AUTH_TYPE=0x03;
+var COMMAND_TYPE=0x02;
+var AUTH_RES_TYPE=0x02;
+var TIMEOUT=2000; //time to wait for a response
+
module.exports.newHandle = function(){
this.socket=null;
this.reqID=1;
@@ -8,42 +13,94 @@ this.online = false;
this.isOnline = function() {return this.online;}//return online status of connection
-//Callbacks
-this.onResponse = function(data){console.log(data)};
-this.onAuthFail = function(){console.log("AUTH FAIL");};
+//queue management
+this.queue = {}; //keeps track of commands sent
+/**
+* Add a call to the queue and start the timeout.
+* @param {Number} Request id
+* @param {Function} callback
+*/
+this._addQueue = function(id, callback){
+ if (typeof(callback)==='undefined')return;
+ var q = this.queue;
+ var t = setTimeout(function(){
+ delete q[id]; //drop the timed out request from the queue
+ callback({error:'Timed out'});
+ },TIMEOUT);
+ this.queue[id]={callback:callback, timeout:t};
+};
+
+/**
+* Check if we have a request matching the response
+* @param {Object} response object
+*/
+this._processQueue = function(response){
+ //do we have anything on the queue that matches.
+ if(response.reqID in this.queue){
+ var q = this.queue[response.reqID];
+ //clear the timeout!
+ clearTimeout(q.timeout);
+ //callback
+ q.callback(null, response);
+ //delete from the queue
+ delete this.queue[response.reqID];
+ }
+}
+
//connect command
-this.connect = function(ip,port,password){
+this.connect = function(ip,port,password,callback){
+ if(typeof(callback)==='undefined'){callback = function(err, response){};}
this.socket = net.createConnection(port,ip);
this.socket.rcon = this;
this.socket.on('close',function(){this.rcon.authed=false;this.rcon.online=false});
this.socket.on('connect',function(){
this.rcon.online=true;
- this.write(this.rcon.makePacket(0,3,password));
+ this.write(this.rcon.makePacket(0,AUTH_TYPE,password));
});
this.socket.on('data',function(data){
- response = this.rcon.readPacket(data);
- if(response.type==2){
- if(response.reqID == -1){
- this.rcon.onAuthFail();
- this.destroy();
- return;
- }else{this.rcon.authed=true;return;}
- }
- this.rcon.onResponse(response);
+ var responses = this.rcon.readPacket(data);
+ for(var i=0; i<responses.length;i++){
+ var response = responses[i];
+ if(response.type === AUTH_RES_TYPE){
+ if(response.reqID == -1){
+ this.destroy();
+ callback('AUTH FAILED');
+ return;
+ }else{
+ this.rcon.authed=true;
+ callback(null, response);
+ return;
+ }
+ }
+ this.rcon._processQueue(response);
+ }
});
};
-this.sendCommand = function(command) {
- if(!this.online){return {"error":'Not Online'};}
- if(!this.authed){return {"error":'Not Authenticated'};}
- this.socket.write(this.makePacket(this.reqID,2,command));
- this.reqID +=1;
- return {"reqID":this.reqID-1};
+
+//end connection
+this.end = function(){
+ this.socket.end();
+}
+
+
+this.sendCommand = function(command,callback) {
+ var msg;
+ if (typeof(callback)==='undefined') {callback = function(err, response){};}
+ if(!this.online){msg={"error":'Not Online'};callback(msg);return msg;}
+ if(!this.authed){msg={"error":'Not Authenticated'};callback(msg); return msg;}
+ var id = this.reqID+0;
+ this.socket.write(this.makePacket(id,COMMAND_TYPE,command));
+ this._addQueue(id, callback);
+
+ this.reqID +=1;
+ return {"reqID":id};
};
+
+
//makes a packet for RCON
this.makePacket = function(req,type,S1){
-
var b = new Buffer(14+S1.length);
b.writeInt32LE(10+S1.length,0);
b.writeInt32LE(req,4);
@@ -55,12 +112,32 @@ this.makePacket = function(req,type,S1){
//read a packet out into a JSON object
this.readPacket = function(packet){
- return {
- reqID: packet.readInt32LE(4),
- type: packet.readInt32LE(8),
- data:packet.toString('utf8',12,packet.length-2)
- };
+ var responses=[];
+ var haveMore=true;
+ var size, res;
+ while(haveMore){
+ size = packet.readInt32LE(0);
+ res = {
+ size: size,
+ reqID: packet.readInt32LE(4),
+ type: packet.readInt32LE(8),
+ data:packet.toString('utf8',12,size+2) //don't read beyond response size
+ };
+ responses.push(res);
+ //there are more to read, at least one additional response
+ if(packet.length > size+4){
+ //position on the next one
+ packet = packet.slice(size+4);
+ }
+ else{
+ //no more responses
+ haveMore=false;
+ }
+ }
+ //always return an array even if only one response
+ return responses;
};
return this;
};
+
View
@@ -1,5 +1,10 @@
{
- "name": "node-rcon"
- , "version": "0.0.1"
- , "main":"node-rcon.js"
+ "name": "rcon",
+ "description": "rcon protocol wrapper for node",
+ "keywords": ["rcon", "protocol"],
+ "version": "0.0.1",
+ "main":"node-rcon.js",
+ "engines": {
+ "node": "~v0.6.0"
+ }
}

0 comments on commit 3c685fb

Please sign in to comment.