gun slinger!

This commit is contained in:
Mark Nadal 2014-09-24 11:08:00 -07:00
parent bfd11b9115
commit cf386e3ced
5 changed files with 248 additions and 67 deletions

View File

@ -1,15 +1,19 @@
console.log("If modules not found, run `npm install` in example/admin folder!"); // git subtree push -P examples/admin heroku master console.log("If modules not found, run `npm install` in example/admin folder!"); // git subtree push -P examples/admin heroku master
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || 8888; var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || 8888;
var express = require('express'); var express = require('connect');
var bodyParser = require('body-parser'); var bodyParser = require('body-parser');
var app = express(); var app = express();
var Gun = require('gun'); var Gun = require('gun');
var gun = Gun({ var gun = Gun({
s3: (process.env.NODE_ENV === 'production')? null : require('../../test/shotgun') // replace this with your own keys! s3: (process.env.NODE_ENV === 'production')? null : require('../../test/shotgun') // replace this with your own keys!
}); });
app.use(function(req, res, next){
app.use(gun.server) console.log("THIS HIT SEEEERVER", req.url);
.use(express.static(__dirname)) next();
})
.use(gun.server)
.use(require('serve-static')(__dirname))
//.use(express.static(__dirname))
app.listen(port); app.listen(port);
console.log('Express started on port ' + port + ' with /gun'); console.log('Express started on port ' + port + ' with /gun');

View File

@ -29,52 +29,183 @@
.none { .none {
display: none; display: none;
} }
.fight {
z-index: 9999;
background: brown;
color: white;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.player input, .player button {
font-size: 18pt;
}
</style> </style>
<h2>Gun Duel!</h2> <h2><i>GUNSLINGER</i></h2>
<span>Old western cowboy style! Two players are needed, whoever can shoot the other first wins!</span> <span>Old West Duel! Two players are needed, whoever can tap the screen first to draw their pistol and shoot wins!</span><br>
<span><b>Fastest gun in the west, <span name="fastest">nut'n</span> by <span name="slinger">nobody</span>.</b></span> <span><b>Fastest gun in the west, <span name="fastest">nut'n</span> seconds, by <span name="slinger">nobody</span>.</b></span>
<form id="p1" class="assign-player" onsubmit="return false;"> <form id="p1" class="player" onsubmit="return false;">
Player 1: <input type="text" name="p1" class="player" placeholder="nickname"> Player 1: <input type="text" name="p1" placeholder="nickname"> <button type="submit">Join!</button>
<input type="submit" class="take" value="Join!">
</form> </form>
<form id="p2" class="assign-player" onsubmit="return false;"> <form id="p2" class="player" onsubmit="return false;">
Player 2: <input type="text" name="p2" class="player" placeholder="nickname"> Player 2: <input type="text" name="p2" placeholder="nickname"> <button type="submit">Join!</button>
<input type="submit" class="take" value="Join!">
</form> </form>
<div id="duel"> <div id="duel" class="none fight">
<center>
<h2>GET READY!</h2>
<button id="reset" class="none">
Reset the game!
</button>
</center>
</div> </div>
<span><b>Last duel won by <span name="last">no one</span> in <span name="ended">0</span> seconds against <span name="loser">nobody</span>.</b></span>
<script> <script>
(function(){ $(function(){
var me = {}, var me = window.me = {},
gun = Gun(['http://localhost:8888/' + 'gun']) game = window.game = {},
.load('game/duel', function(game){ //gun = window.gun = Gun(['http://localhost:8888/' + 'gun'])
console.log(game); gun = window.gun = Gun(['http://gunduel.t.proxylocal.com/' + 'gun'])
$(document).on('submit', '.assign-player', function(e){ .load('game/duel', function(data){
console.log(data);
$(document).on('submit', '.player', function(e){
e.preventDefault(); e.preventDefault();
var nick = $(this).find('input').val(); var nick = $(this).find('input').val(), id = this.id;
if(!nick){ return } if(!nick || me.player){ return }
gun.path(this.id).get(function(val){ gun.path(id).get(function(val){
if(val){ return } if(val){ return }
gun.path(this.id).set(me.player = nick); this.set(me.player = nick);
me.took = id;
}); });
}) })
Gun.on(game._[Gun.sym.id]).event(function(node){ Gun.on(data._[Gun.sym.id]).event(function(node, local){
if(!node){ return }
console.log("change!", node); console.log("change!", node);
if(node.start){ game.schedule(node.start) }
if(node.dqed){ game.dq(node.dqed) }
if(node.sling){ game.sling(node.sling, local) }
Gun.obj.map(node, game.set);
if(node.p1 || node.p2){
gun.path('p1').get(function(p1){ // start game?
if(!p1){ return }
gun.path('p2').get(function(p2){
if(!p2){ return }
game.play(p2);
})
});
}
game.timeout(true);
}); });
Gun.obj.map(game, me.set); Gun.obj.map(data, game.set);
game.timeout(20);
}); });
me.set = function(val, name){ game.$duel = $("#duel");
game.$duelm = game.$duel.clone();
game.play = function(p2){
if(!me.player){ return }
game.$duel.removeClass("none");
if(me.player == p2){ return }
me.scheduled = (+new Date()) + Math.round(Math.random() * 2000 + 2700); // MIN is the right number, and MAX is the SUM of both numbers.
gun.path('start').set(me.scheduled);
}
game.schedule = function(at){
console.log(" ------------------ START", at);
if(me.started){ return }
me.started = true;
me.scheduled = at;
Gun.schedule(at, function(){
me.shoot = true;
if(me.dqed){ return }
game.$duel.css({background: 'lime'}).find('h2').text("FIRE!");
});
}
game.fire = function(){
if(!me.started || me.fired){ return }
me.fired = (+ new Date());
if(me.fired < me.scheduled){ // DQ
me.dqed = me.player;
gun.path('dqed').set(me.player);
return;
}
gun.path('dqed').get(function(yes){
if(yes){ return }
game.$duel.css({background: 'gold'}).find('h2').text("STOP!");
gun.path('sling').set(me.player);
me.time = (me.fired - me.scheduled) / 1000; // in seconds
});
}
game.sling = function(){
me.count = (me.count || 0) + 1;
if(me.count < 2){ return }
$('#reset').removeClass('none');
gun.path('sling').get(function(sling){
if(!sling){ return }
if(sling == me.player){
game.$duel.css({background: 'red'}).find('h2').text("YOU DIED!!!");
gun.path('loser').set(me.player);
return;
}
game.$duel.css({background: 'skyblue'}).find('h2').text("YOU WON!!!");
if(!me.time || me.fired < me.scheduled || me.time < 0){ return }
gun.path('last').set(me.player);
gun.path('ended').set(me.time);
gun.path('fastest').get(function(time){
if(time <= me.time){ return }
gun.path('fastest').set(me.time);
gun.path('slinger').set(me.player);
});
});
}
game.dq = function(who){
$('#reset').removeClass('none');
if(who == me.player){
return game.$duel.css({background: 'gray'}).find('h2').text("DISQUALIFIED!!!");
}
if(me.dqed){
return game.$duel.find('h2').text("BOTH DISQUALIFIED!");
}
game.$duel.css({background: 'skyblue'}).find('h2').text("YOU WON!!!");
}
game.clear = function(){
me = {};
game.$duel.replaceWith(game.$duel = game.$duelm.clone());
gun.path('p1').set("");
gun.path('p2').set("");
gun.path('sling').set(null);
gun.path('start').set(null);
gun.path('dq').set(null);
game.$duel.addClass('none');
}
game.timeout = function(wait){
if(true === wait){ return clearTimeout(me.timeout) }
wait = wait || 15;
clearTimeout(me.timeout);
me.timeout = setTimeout(game.clear, wait * 1000);
}
game.set = function(val, name){
if(val == game.nothing){ return }
$("[name='" + name + "']").text(val).val(val); $("[name='" + name + "']").text(val).val(val);
Gun.on("duel-" + name).emit(val, name); Gun.on("duel-" + name).emit(val, name);
} }
me.plock = function(val, id){ game.plock = function(val, id){
console.log("OH?", val, id); $("#" + id).find('input').attr("readonly", val? true : false);
$("#" + id).find('.player').attr("readonly", val? true : false); $("#" + id).find('button').text(val? "Taken!" : "Join!").attr("disabled", val? true : false);
$("#" + id).find('.take').val(val? "Taken!" : "Join!").attr("disabled", val? true : false);
} }
Gun.on("duel-p1").event(me.plock); Gun.on("duel-p1").event(game.plock);
Gun.on("duel-p2").event(me.plock); Gun.on("duel-p2").event(game.plock);
$(document).on('click', '#duel', game.fire);
$(document).on('click', '#reset', game.clear);
}());
</script>
<script>
(function(){
return;
var game = Gun(location + 'gun').load('game/duel');
game.on('change').path('p1').or.path('p2').get(function(val){
$("#" + this.field).find('input').val(val? "Taken!" : "Join!").attr("disabled", val? true : false);
$("#" + this.field).find('button').val(val? "Taken!" : "Join!").attr("disabled", val? true : false);
});
}()); }());
</script> </script>
</body> </body>

79
gun.js
View File

@ -95,8 +95,8 @@
} }
Gun.chain.path = function(path){ // The focal point follows the path Gun.chain.path = function(path){ // The focal point follows the path
var gun = this.chain(); var gun = this.chain();
path = path.split('.'); path = (path||'').split('.');
Gun.log("PATH stack trace", gun._.events.trace + 1, 'was it before loaded?', this._); Gun.log("PATH stack trace", path, gun._.events.trace + 1, 'was it before loaded?', this._);
gun._.events.on(gun._.events.trace += 1).event(function trace(node){ gun._.events.on(gun._.events.trace += 1).event(function trace(node){
Gun.log("stack at", gun._.events.at); Gun.log("stack at", gun._.events.at);
if(!path.length){ // if the path resolves to another node, we finish here if(!path.length){ // if the path resolves to another node, we finish here
@ -131,9 +131,9 @@
gun._.events.on(gun._.events.trace += 1).event(function(node){ gun._.events.on(gun._.events.trace += 1).event(function(node){
console.log("BOOM got it", node); console.log("BOOM got it", node);
if(gun._.field){ if(gun._.field){
return cb((node||{})[gun._.field]); // copy data first? return cb.call(gun, (node||{})[gun._.field]); // copy data first?
} }
cb(Gun.obj.copy(node)); // we do here. cb.call(gun, Gun.obj.copy(node)); // we do here.
}); });
if(gun._.loaded){ if(gun._.loaded){
gun._.events.at -= 1; // IDK why we are doing this, just trying to get something to work. gun._.events.at -= 1; // IDK why we are doing this, just trying to get something to work.
@ -428,7 +428,7 @@
var serverState = Gun.time.is(); var serverState = Gun.time.is();
// add more checks? // add more checks?
var state = HAM(serverState, deltaStates[field], states[field], deltaValue, current[field]); var state = HAM(serverState, deltaStates[field], states[field], deltaValue, current[field]);
// Gun.log("HAM:", field, deltaValue, deltaStates[field], current[field], 'the', state, (deltaStates[field] - serverState)); //console.log("HAM:", field, deltaValue, deltaStates[field], current[field], 'the', state, (deltaStates[field] - serverState));
if(state.err){ if(state.err){
Gun.log(".!HYPOTHETICAL AMNESIA MACHINE ERR!.", state.err); Gun.log(".!HYPOTHETICAL AMNESIA MACHINE ERR!.", state.err);
return; return;
@ -615,7 +615,7 @@
} }
}()); }());
Gun.log = function(a, b, c, d, e, f){ //s, l){ Gun.log = function(a, b, c, d, e, f){ //s, l){
console.log(a, b, c, d, e, f); //console.log(a, b, c, d, e, f);
//console.log.apply(console, arguments); //console.log.apply(console, arguments);
} }
own.sym = Gun.sym = { own.sym = Gun.sym = {
@ -637,7 +637,7 @@
tab.server = tab.server || function(req, res, next){ tab.server = tab.server || function(req, res, next){
} }
window.tab = tab; //window.XMLHttpRequest = null; // for debugging purposes window.tab = tab; // window.XMLHttpRequest = null; // for debugging purposes
(function(){ (function(){
tab.store = {}; tab.store = {};
var store = window.localStorage || {setItem: function(){}, removeItem: function(){}, getItem: function(){}}; var store = window.localStorage || {setItem: function(){}, removeItem: function(){}, getItem: function(){}};
@ -659,7 +659,7 @@
} }
(function(){ (function(){
tab.subscribe.sub = (reply.headers || {})['gun-sub']; tab.subscribe.sub = (reply.headers || {})['gun-sub'] || tab.subscribe.sub;
//console.log("We are sub", tab.subscribe.sub); //console.log("We are sub", tab.subscribe.sub);
var data = reply.body; var data = reply.body;
if(!data || !data._){ return } if(!data || !data._){ return }
@ -668,23 +668,44 @@
}, {headers: {'Gun-Sub': tab.subscribe.sub || ''}, header: {'Gun-Sub': 1}}); }, {headers: {'Gun-Sub': tab.subscribe.sub || ''}, header: {'Gun-Sub': 1}});
}); });
} }
tab.url = function(nodes){
return;
console.log("urlify delta", nodes);
var s = ''
, uri = encodeURIComponent;
Gun.obj.map(nodes, function(delta, id){
var ham;
if(!delta || !delta._ || !(ham = delta._[Gun.sym.HAM])){ return }
s += uri('#') + '=' + uri(id) + '&';
Gun.obj.map(delta, function(val, field){
if(field === Gun.sym.meta){ return }
s += uri(field) + '=' + uri(Gun.text.ify(val)) + uri('>') + uri(ham[field]) + '&';
})
});
console.log(s);
return s;
}
tab.set = tab.set || function(nodes, cb){ tab.set = tab.set || function(nodes, cb){
cb = cb || function(){}; cb = cb || function(){};
// TODO: batch and throttle later. // TODO: batch and throttle later.
tab.store.set(respond.id = 'send/' + Gun.text.random(), nodes); tab.store.set(cb.id = 'send/' + Gun.text.random(), nodes);
//console.log("localStorage the DELTA", nodes); //tab.url(nodes);
Gun.obj.map(gun.__.opt.peers, function(peer, url){ Gun.obj.map(gun.__.opt.peers, function(peer, url){
tab.ajax(url, nodes, respond, {headers: {'Gun-Sub': tab.subscribe.sub || ''}}); tab.ajax(url, nodes, function respond(err, reply, id){
}); var body = reply && reply.body;
function respond(err, reply, id){ respond.id = respond.id || cb.id;
if(reply && reply.body){ Gun.obj.del(tab.set.defer, id); // handle err with a retry? Or make a system auto-do it?
if(reply.body.defer){ if(!body){ return }
tab.set.defer[reply.body.defer] = respond; if(body.defer){
console.log("deferring post", body.defer);
tab.set.defer[body.defer] = respond;
} }
if(reply.body.refed || reply.body.reply){ if(body.reply){
//console.log("-------post-reply-all--------->", reply, err); respond(null, {headers: reply.headers, body: body.reply });
respond(null, {headers: reply.headers, body: reply}); }
Gun.obj.map(reply.body.refed, function(r, id){ if(body.refed){
console.log("-------post-reply-all--------->", reply, err);
Gun.obj.map(body.refed, function(r, id){
var cb; var cb;
if(cb = tab.set.defer[id]){ if(cb = tab.set.defer[id]){
cb(null, {headers: reply.headers, body: r}, id); cb(null, {headers: reply.headers, body: r}, id);
@ -693,11 +714,13 @@
// TODO: should be able to do some type of "checksum" that every request cleared, and if not, figure out what is wrong/wait for finish. // TODO: should be able to do some type of "checksum" that every request cleared, and if not, figure out what is wrong/wait for finish.
return; return;
} }
console.log('callback complete, now respond', respond.id); if(body.reply || body.defer || body.refed){ return }
tab.store.del(respond.id); tab.store.del(respond.id);
} }, {headers: {'Gun-Sub': tab.subscribe.sub || ''}});
Gun.obj.del(tab.set.defer, id); });
} Gun.obj.map(nodes, function(node, id){
Gun.on(id).emit(node, true); // TODO: Temporary hack, I want to rebroadcast back to ourselves. IDK if this is always useful, and we shouldn't use global.
});
} }
tab.set.defer = {}; tab.set.defer = {};
tab.subscribe = function(id){ // TODO: BUG!!! ERROR! Handle disconnection (onerror)!!!! tab.subscribe = function(id){ // TODO: BUG!!! ERROR! Handle disconnection (onerror)!!!!
@ -711,10 +734,14 @@
'Gun-Sub': tab.subscribe.sub || '' 'Gun-Sub': tab.subscribe.sub || ''
} }
}, query = tab.subscribe.sub? '' : tab.subscribe.query(tab.subscribe.to); }, query = tab.subscribe.sub? '' : tab.subscribe.query(tab.subscribe.to);
console.log("subscribing poll", tab.subscribe.sub);
Gun.obj.map(gun.__.opt.peers, function(peer, url){ Gun.obj.map(gun.__.opt.peers, function(peer, url){
tab.ajax(url + query, null, function(err, reply){ tab.ajax(url + query, null, function(err, reply){
//console.log("poll", err, reply); if(err || !reply || !reply.body || reply.body.err){ // not interested in any null/0/''/undefined values
if(!reply || !reply.body){ return } // not interested in any null/0/''/undefined values console.log(err, reply);
return;
}
console.log("poll", reply);
tab.subscribe.poll(); tab.subscribe.poll();
if(reply.headers){ if(reply.headers){
tab.subscribe.sub = reply.headers['gun-sub'] || tab.subscribe.sub; tab.subscribe.sub = reply.headers['gun-sub'] || tab.subscribe.sub;

View File

@ -1,5 +1,5 @@
{ "name": "gun" { "name": "gun"
, "version": "0.0.6" , "version": "0.0.6a"
, "author": "Mark Nadal" , "author": "Mark Nadal"
, "description": "Graph engine." , "description": "Graph engine."
, "engines": { , "engines": {

View File

@ -6,6 +6,7 @@
, meta = {}; , meta = {};
Gun.on('opt').event(function(gun, opt){ Gun.on('opt').event(function(gun, opt){
gun.server = gun.server || function(req, res, next){ // this whole function needs refactoring and modularization gun.server = gun.server || function(req, res, next){ // this whole function needs refactoring and modularization
//console.log("\n\n GUN SERVER!");
next = next || function(){}; next = next || function(){};
if(!req || !res){ return next() } if(!req || !res){ return next() }
if(!req.url){ return next() } if(!req.url){ return next() }
@ -66,6 +67,7 @@
s3.prenode = s3.prenode || opt.s3.prenode || '_/nodes/'; s3.prenode = s3.prenode || opt.s3.prenode || '_/nodes/';
gun.__.opt.batch = opt.batch || gun.__.opt.batch || 10; gun.__.opt.batch = opt.batch || gun.__.opt.batch || 10;
gun.__.opt.throttle = opt.throttle || gun.__.opt.throttle || 15; gun.__.opt.throttle = opt.throttle || gun.__.opt.throttle || 15;
gun.__.opt.disconnect = opt.disconnect || gun.__.opt.disconnect || 5;
if(!gun.__.opt.keepMaxSockets){ require('https').globalAgent.maxSockets = require('http').globalAgent.maxSockets = Infinity } // WARNING: Document this! if(!gun.__.opt.keepMaxSockets){ require('https').globalAgent.maxSockets = require('http').globalAgent.maxSockets = Infinity } // WARNING: Document this!
s3.load = s3.load || function(key, cb, opt){ s3.load = s3.load || function(key, cb, opt){
@ -160,11 +162,12 @@
gun.server.transport = (function(){ gun.server.transport = (function(){
function tran(req, cb){ function tran(req, cb){
//console.log("\n\n\n", req); //console.log(req);
req.sub = req.headers['gun-sub']; // grab the sub req.sub = req.headers['gun-sub']; // grab the sub
req.tab = tran.sub.s[req.sub] || {}; // check to see if there already is a tab associated with it, or create one req.tab = tran.sub.s[req.sub] || {}; // check to see if there already is a tab associated with it, or create one
req.tab.sub = req.sub = req.sub || Gun.text.random(); // Generate a session id if we don't already have one req.tab.sub = req.sub = req.sub || Gun.text.random(); // Generate a session id if we don't already have one
req.tran = tran.xhr(req, cb) || tran.jsonp(req, cb); // polyfill transport layer req.tran = tran.xhr(req, cb) || tran.jsonp(req, cb); // polyfill transport layer
clearTimeout(req.tab.timeout);
// raw test for now, no auth: // raw test for now, no auth:
if(!req.tran){ return cb({headers: {"Content-Type": tran.json}, body: {err: "No transport layer!"}}) } if(!req.tran){ return cb({headers: {"Content-Type": tran.json}, body: {err: "No transport layer!"}}) }
if('post' === req.method || 'patch' === req.method){ return tran.post(req, req.tran) } // TODO: Handle JSONP emulated POST via GET if('post' === req.method || 'patch' === req.method){ return tran.post(req, req.tran) } // TODO: Handle JSONP emulated POST via GET
@ -204,7 +207,7 @@
if(context.err){ return cb({body: {err: context.err}}) } if(context.err){ return cb({body: {err: context.err}}) }
// WARNING! TODO: BUG! Do not send OK confirmation if amnesiaQuaratine is activated! Not until after it has actually been processed!!! // WARNING! TODO: BUG! Do not send OK confirmation if amnesiaQuaratine is activated! Not until after it has actually been processed!!!
if(Gun.fns.is(gun.__.opt.hooks.set)){ if(Gun.fns.is(gun.__.opt.hooks.set)){
gun.__.opt.hooks.set(context.nodes, function(err, data){ // now iterate through those nodes to S3 and get a callback once all are saved gun.__.opt.hooks.set(context.nodes, function saved(err, data){ // now iterate through those nodes to S3 and get a callback once all are saved
var body = {}; var body = {};
if(err){ if(err){
body.err = err ; body.err = err ;
@ -217,6 +220,7 @@
} }
var now = tran.post.s[req.sub]; // begin our stupid Chrome fix, we should abstract this out into defer (where it belogns) to keep things clean. var now = tran.post.s[req.sub]; // begin our stupid Chrome fix, we should abstract this out into defer (where it belogns) to keep things clean.
if(!now){ return } // utoh we've lost our reply to the tab! if(!now){ return } // utoh we've lost our reply to the tab!
clearTimeout(now.timeout);
now.body = now.body || {}; // make sure we have a body for our multi-response in a single response. now.body = now.body || {}; // make sure we have a body for our multi-response in a single response.
if(req.wait){ // did this request get deferred? if(req.wait){ // did this request get deferred?
(now.body.refed = now.body.refed || {})[req.wait] = err? {err: err} : defer.map({}, context.nodes, 1); // then reply to it "here". (now.body.refed = now.body.refed || {})[req.wait] = err? {err: err} : defer.map({}, context.nodes, 1); // then reply to it "here".
@ -224,7 +228,7 @@
now.body.reply = err? {err: err} : defer.map({}, context.nodes, 1); // else this is the original POST that had to be upgraded. now.body.reply = err? {err: err} : defer.map({}, context.nodes, 1); // else this is the original POST that had to be upgraded.
} }
if(0 < (now.count = ((now.count || 0) - 1))){ // Don't reply till all deferred POSTs have successfully heard back from S3. (Sarcasm: Like counting guarantees that) if(0 < (now.count = ((now.count || 0) - 1))){ // Don't reply till all deferred POSTs have successfully heard back from S3. (Sarcasm: Like counting guarantees that)
return; // TODO: BUG!!! Memory leak, we have no guarantee we'll ever get a reply! So time it out, maybe some multiple of the S3 throttle. return now.timeout = setTimeout(saved, gun.__.opt.throttle * 2 * 1000); // reply not guaranteed, so time it out, in seconds.
} }
if(Gun.fns.is(now)){ if(Gun.fns.is(now)){
now({body: now.body}); // FINALLY reply for ALL the POSTs for that session that accumulated. now({body: now.body}); // FINALLY reply for ALL the POSTs for that session that accumulated.
@ -278,6 +282,7 @@
//console.log("<-- ", req.sub, req.tran ," -->"); //console.log("<-- ", req.sub, req.tran ," -->");
req.tab = tran.sub.s[req.sub]; req.tab = tran.sub.s[req.sub];
if(!req.tab){ if(!req.tab){
console.log(req.url.query);
cb({ cb({
headers: {'Gun-Sub': ''} headers: {'Gun-Sub': ''}
,body: {err: "Please re-initialize sub."} ,body: {err: "Please re-initialize sub."}
@ -286,6 +291,7 @@
} }
//console.log("\n\n\n THE CURRENT STATUS IS");console.log(req.tab); //console.log("\n\n\n THE CURRENT STATUS IS");console.log(req.tab);
if(req.tab.queue && req.tab.queue.length){ if(req.tab.queue && req.tab.queue.length){
tran.clean(req.tab); // We flush their data now, if they don't come back for more within timeout, we remove their session
console.log("_____ NOW PUSHING YOUR DATA ______", req.sub); console.log("_____ NOW PUSHING YOUR DATA ______", req.sub);
cb({ headers: {'Gun-Sub': req.sub} }); cb({ headers: {'Gun-Sub': req.sub} });
while(1 < req.tab.queue.length){ while(1 < req.tab.queue.length){
@ -299,7 +305,18 @@
} }
} }
tran.sub.s = {}; tran.sub.s = {};
tran.sub.scribe = function(tab, id){ // TODO: BUG!!! Memory leaks, remember to destroy sessions via timeout. tran.clean = function(tab, mult){
if(!tab){ return }
mult = mult || 1;
clearTimeout(tab.timeout);
tab.timeout = setTimeout(function(){
if(!tab){ return }
if(tab.reply){ tab.reply({body: {err: "Connection timed out"}}) }
console.log("!!!! DISCONNECTING CLIENT !!!!!", tab.sub);
Gun.obj.del(tran.sub.s, tab.sub)
}, gun.__.opt.disconnect * mult * 1000); // in seconds
}
tran.sub.scribe = function(tab, id){
tran.sub.s[tab.sub] = tab; tran.sub.s[tab.sub] = tab;
tab.subs = tab.subs || {}; tab.subs = tab.subs || {};
tab.subs[id] = tab.subs[id] || tran.push.on(id).event(function(req){ tab.subs[id] = tab.subs[id] || tran.push.on(id).event(function(req){
@ -307,7 +324,8 @@
if(!tab){ return this.off() } // resolve any dangling callbacks if(!tab){ return this.off() } // resolve any dangling callbacks
req.sub = req.sub || req.headers['gun-sub']; req.sub = req.sub || req.headers['gun-sub'];
if(req.sub === tab.sub){ return } // do not send back to the tab that sent it if(req.sub === tab.sub){ return } // do not send back to the tab that sent it
console.log('FROM:', req.sub, "TO:", tab); console.log('FROM:', req.sub, "TO:", tab.sub);
tran.clean(tab);
if(tab.reply){ if(tab.reply){
tab.reply({ tab.reply({
headers: {'Gun-Sub': tab.sub} headers: {'Gun-Sub': tab.sub}
@ -318,6 +336,7 @@
} }
(tab.queue = tab.queue || []).push(req.body); (tab.queue = tab.queue || []).push(req.body);
}); });
tran.clean(tab, 2);
} }
tran.xhr = function(req, cb){ // Streaming Long Polling tran.xhr = function(req, cb){ // Streaming Long Polling
return req.tran || (req.headers['x-requested-with'] === 'XMLHttpRequest'? transport : null); return req.tran || (req.headers['x-requested-with'] === 'XMLHttpRequest'? transport : null);