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) { |