Mercurial > hg > rlgwebd
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) { |
