comparison rlgwebd.js @ 40:f7116eb3f791

rlgwebd.js: refactor some game-starting code. Separate things like checking for games in progress, starting a new game, and figuring out where the lockfile and ttyrec should go. This allows e.g. the games-in-progress check to be used to create status messages. It also keeps the argument list for the TermSession constructor to a sensible size.
author John "Elwin" Edwards <elwin@sdf.org>
date Fri, 08 Jun 2012 18:11:47 -0700
parents e8ac0e3d2614
children 8f6bc0df58fa
comparison
equal deleted inserted replaced
39:e8ac0e3d2614 40:f7116eb3f791
52 52
53 /* Constructor for TermSessions. Note that it opens the terminal and 53 /* Constructor for TermSessions. Note that it opens the terminal and
54 * adds itself to the sessions dict. It currently assumes the user has 54 * adds itself to the sessions dict. It currently assumes the user has
55 * been authenticated. 55 * been authenticated.
56 */ 56 */
57 function TermSession(game, user, files, dims) { 57 /* TODO take a callback, or emit success/err events. */
58 function TermSession(game, user, dims) {
58 /* First make sure starting the game will work. */ 59 /* First make sure starting the game will work. */
59 if (game in games) { 60 if (game in games) {
60 this.game = games[game]; 61 this.game = games[game];
61 } 62 }
62 else { 63 else {
63 // TODO: throw an exception instead 64 // TODO: throw an exception instead
64 return null; 65 return null;
65 } 66 }
66 this.player = user; 67 this.player = String(user);
67 /* This order seems to best avoid race conditions... */ 68 /* This order seems to best avoid race conditions... */
68 this.alive = false; 69 this.alive = false;
69 this.sessid = randkey(2); 70 this.sessid = randkey(2);
70 while (this.sessid in sessions) { 71 while (this.sessid in sessions) {
71 this.sessid = randkey(2); 72 this.sessid = randkey(2);
95 args = [this.game.path, "-n", user.toString()]; 96 args = [this.game.path, "-n", user.toString()];
96 this.child = child_process.spawn("/bin/ptyhelper", args, {"env": childenv}); 97 this.child = child_process.spawn("/bin/ptyhelper", args, {"env": childenv});
97 var ss = this; 98 var ss = this;
98 this.alive = true; 99 this.alive = true;
99 this.data = []; 100 this.data = [];
100 this.lock = files[0]; 101 /* Set up the lockfile and ttyrec */
101 fs.writeFile(this.lock, this.child.pid.toString() + '\n' + this.w + '\n' + 102 var ts = timestamp();
102 this.h + '\n', "utf8"); 103 var progressdir = "/dgldir/inprogress-" + this.game.uname;
103 this.record = fs.createWriteStream(files[1], { mode: 0664 }); 104 this.lock = path.join(progressdir, this.player + ":node:" + ts + ".ttyrec");
105 var lmsg = this.child.pid.toString() + '\n' + this.w + '\n' + this.h + '\n';
106 fs.writeFile(this.lock, lmsg, "utf8");
107 var ttyrec = path.join("/dgldir/ttyrec", this.player, this.game.uname,
108 ts + ".ttyrec");
109 this.record = fs.createWriteStream(ttyrec, { mode: 0664 });
104 /* END setup */ 110 /* END setup */
105 function ttyrec_chunk(buf) { 111 function ttyrec_chunk(buf) {
106 var ts = new Date(); 112 var ts = new Date();
107 var chunk = new Buffer(buf.length + 12); 113 var chunk = new Buffer(buf.length + 12);
108 /* TTYREC headers */ 114 /* TTYREC headers */
217 delete sessions[id]; 223 delete sessions[id];
218 tslog("Session %s removed.", id); 224 tslog("Session %s removed.", id);
219 }; 225 };
220 } 226 }
221 227
228 function checkprogress(user, game, callback, args) {
229 var progressdir = "/dgldir/inprogress-" + game.uname;
230 fs.readdir(progressdir, function(err, files) {
231 if (err) {
232 args.unshift(err, null);
233 callback.apply(null, args);
234 return;
235 }
236 var fre = RegExp("^" + user + ":");
237 for (var i = 0; i < files.length; i++) {
238 if (files[i].match(fre)) {
239 args.shift(null, files[i]);
240 callback.apply(null, args);
241 return;
242 }
243 }
244 args.shift(null, false);
245 callback.apply(null, args);
246 });
247 }
248
222 /* A few utility functions */ 249 /* A few utility functions */
223 function timestamp() { 250 function timestamp() {
224 dd = new Date(); 251 dd = new Date();
225 sd = dd.toISOString(); 252 sd = dd.toISOString();
226 sd = sd.slice(0, sd.indexOf(".")); 253 sd = sd.slice(0, sd.indexOf("."));
372 if (!(gname in games)) { 399 if (!(gname in games)) {
373 sendError(res, 2, "No such game: " + gname); 400 sendError(res, 2, "No such game: " + gname);
374 tslog("Request for nonexistant game \"%s\"", gname); 401 tslog("Request for nonexistant game \"%s\"", gname);
375 return; 402 return;
376 } 403 }
377 // check for an existing game 404 // A callback to pass to the game-in-progress checker.
378 var progressdir = "/dgldir/inprogress-" + games[gname].uname; 405 var launch = function(err, fname) {
379 fs.readdir(progressdir, function(err, files) { 406 if (fname) {
380 if (!err) { 407 sendError(res, 4, null);
381 var fre = RegExp("^" + username + ":"); 408 tslog("%s is already playing %s", username, gname);
382 for (var i = 0; i < files.length; i++) { 409 return;
383 if (files[i].match(fre)) {
384 sendError(res, 4, null);
385 tslog("%s is already playing %s", username, gname);
386 return;
387 }
388 }
389 } 410 }
390 // Game starting has been approved. 411 // Game starting has been approved.
391 var ts = timestamp(); 412 var nsession = new TermSession(gname, username, dims);
392 var lockfile = path.join(progressdir, username + ":node:" + ts + ".ttyrec");
393 var ttyrec = path.join("/dgldir/ttyrec", username, gname, ts + ".ttyrec");
394 var nsession = new TermSession(gname, username, [lockfile, ttyrec], dims);
395 if (nsession) { 413 if (nsession) {
396 /* Technically there's a race condition for the "lock"file, but since
397 * it requires the user deliberately starting two games at similar times,
398 * it's not too serious. We can't get O_EXCL in Node anyway. */
399 res.writeHead(200, {'Content-Type': 'application/json'}); 414 res.writeHead(200, {'Content-Type': 'application/json'});
400 var reply = {"t": "l", "id": nsession.sessid, "w": nsession.w, "h": 415 var reply = {"t": "l", "id": nsession.sessid, "w": nsession.w, "h":
401 nsession.h}; 416 nsession.h};
402 res.write(JSON.stringify(reply)); 417 res.write(JSON.stringify(reply));
403 res.end(); 418 res.end();
406 } 421 }
407 else { 422 else {
408 sendError(res, 5, "Failed to open TTY"); 423 sendError(res, 5, "Failed to open TTY");
409 tslog("Unable to allocate TTY for %s", gname); 424 tslog("Unable to allocate TTY for %s", gname);
410 } 425 }
411 }); 426 }
427 checkprogress(username, games[gname], launch, []);
412 } 428 }
413 429
414 /* Sets things up for a new user, like dgamelaunch's commands[register] */ 430 /* Sets things up for a new user, like dgamelaunch's commands[register] */
415 function regsetup(username) { 431 function regsetup(username) {
416 function regsetup_l2(err) { 432 function regsetup_l2(err) {