comparison rlgwebd.js @ 185:bbfda4a4eb7f

Finish moving DglSession methods into the prototype.
author John "Elwin" Edwards
date Tue, 20 Jan 2015 10:17:05 -0500
parents ecedc6f7e4ac
children 11b7baed2e21
comparison
equal deleted inserted replaced
184:ecedc6f7e4ac 185:bbfda4a4eb7f
268 delete sessions[tag]; 268 delete sessions[tag];
269 tslog("Game %s ended.", tag); 269 tslog("Game %s ended.", tag);
270 }; 270 };
271 271
272 function DglSession(filename) { 272 function DglSession(filename) {
273 var ss = this;
274 BaseGame.call(this); 273 BaseGame.call(this);
275 var pathcoms = filename.split('/'); 274 var pathcoms = filename.split('/');
276 this.gname = pathcoms[pathcoms.length - 2]; 275 this.gname = pathcoms[pathcoms.length - 2];
277 if (!(this.gname in games)) { 276 if (!(this.gname in games)) {
278 ss.emit('open', false); 277 this.emit('open', false);
279 return; 278 return;
280 } 279 }
281 var basename = pathcoms[pathcoms.length - 1]; 280 var basename = pathcoms[pathcoms.length - 1];
282 var firstsep = basename.indexOf(':'); 281 var firstsep = basename.indexOf(':');
283 this.pname = basename.slice(0, firstsep); 282 this.pname = basename.slice(0, firstsep);
285 this.ttyrec = path.join("/dgldir/ttyrec", this.pname, this.gname, fname); 284 this.ttyrec = path.join("/dgldir/ttyrec", this.pname, this.gname, fname);
286 /* Flag to prevent multiple handlers from reading simultaneously and 285 /* Flag to prevent multiple handlers from reading simultaneously and
287 * getting into a race. */ 286 * getting into a race. */
288 this.reading = false; 287 this.reading = false;
289 this.rpos = 0; 288 this.rpos = 0;
290 this.readchunk = function () { 289 fs.readFile(filename, {encoding: "utf8"}, (function (err, data) {
291 if (this.reading) 290 if (err) {
291 this.emit('open', false);
292 return; 292 return;
293 this.reading = true;
294 var header = new Buffer(12);
295 fs.read(ss.fd, header, 0, 12, ss.rpos, function (err, n, buf) {
296 /* Stop recursion if end of file has been reached. */
297 if (err || n < 12) {
298 if (!err && n > 0) {
299 tslog("DGL %s: expected 12-byte header, got %d", ss.tag(), n);
300 }
301 ss.reading = false;
302 return;
303 }
304 ss.rpos += 12;
305 /* Update timestamp, to within 1 second. */
306 ss.lasttime = new Date(1000 * buf.readUInt32LE(0));
307 var datalen = buf.readUInt32LE(8);
308 if (datalen > 16384) {
309 // Something is probably wrong...
310 tslog("DGL %s: looking for %d bytes", ss.tag(), datalen);
311 }
312 var databuf = new Buffer(datalen);
313 fs.read(ss.fd, databuf, 0, datalen, ss.rpos, function (err, n, buf) {
314 if (err || n < datalen) {
315 /* Next time, read the header again. */
316 ss.rpos -= 12;
317 ss.reading = false;
318 tslog("DGL %s: expected %d bytes, got %d", ss.tag(), datalen, n);
319 return;
320 }
321 ss.rpos += n;
322 ss.reading = false;
323 /* Process the data */
324 ss.framepush(buf);
325 var wmsg = JSON.stringify({"t": "d", "d": buf.toString("hex")});
326 for (var i = 0; i < ss.watchers.length; i++) {
327 if (ss.watchers[i].connected)
328 ss.watchers[i].sendUTF(wmsg);
329 }
330 ss.emit("data", buf);
331 //tslog("DGL %s: %d bytes", ss.tag(), buf.length);
332 /* Recurse. */
333 ss.readchunk();
334 });
335 });
336 };
337 fs.readFile(filename, {encoding: "utf8"}, function (err, data) {
338 if (err) {
339 ss.emit('open', false);
340 return;
341 } 293 }
342 var lines = data.split('\n'); 294 var lines = data.split('\n');
343 ss.h = Number(lines[1]); 295 this.h = Number(lines[1]);
344 ss.w = Number(lines[2]); 296 this.w = Number(lines[2]);
345 fs.open(ss.ttyrec, "r", function(err, fd) { 297 fs.open(this.ttyrec, "r", (function (err, fd) {
346 if (err) { 298 if (err) {
347 ss.emit('open', false); 299 this.emit('open', false);
348 } 300 }
349 else { 301 else {
350 ss.fd = fd; 302 this.fd = fd;
351 ss.emit('open', true); 303 this.emit('open', true);
352 tslog("DGL %s: open", ss.tag()); 304 tslog("DGL %s: open", this.tag());
353 gamemux.emit('begin', ss.gname, ss.pname, 'dgl'); 305 gamemux.emit('begin', this.gname, this.pname, 'dgl');
354 ss.readchunk(); 306 this.startchunk();
355 ss.recwatcher = fs.watch(ss.ttyrec, function (ev, finame) { 307 this.recwatcher = fs.watch(this.ttyrec, this.notifier.bind(this));
356 if (ev == "change") 308 }
357 ss.readchunk(); 309 }).bind(this));
358 }); 310 }).bind(this));
359 }
360 });
361 });
362 this.close = function () {
363 this.recwatcher.close()
364 /* Ensure all data is handled before quitting. */
365 this.readchunk();
366 var connlist = this.watchers;
367 this.watchers = [];
368 for (var i = 0; i < connlist.length; i++) {
369 if (connlist[i].connected)
370 connlist[i].close();
371 }
372 fs.close(this.fd);
373 this.emit("close");
374 gamemux.emit('end', this.gname, this.pname);
375 tslog("DGL %s: closed", ss.tag());
376 };
377 } 311 }
378 DglSession.prototype = new BaseGame(); 312 DglSession.prototype = new BaseGame();
313
314 /* 3 functions to get data from the ttyrec file. */
315 DglSession.prototype.startchunk = function () {
316 if (this.reading)
317 return;
318 this.reading = true;
319 var header = new Buffer(12);
320 fs.read(this.fd, header, 0, 12, this.rpos, this.datachunk.bind(this));
321 };
322
323 DglSession.prototype.datachunk = function (err, n, buf) {
324 /* Stop recursion if end of file has been reached. */
325 if (err || n < 12) {
326 if (!err && n > 0) {
327 tslog("DGL %s: expected 12-byte header, got %d", this.tag(), n);
328 }
329 this.reading = false;
330 return;
331 }
332 this.rpos += 12;
333 /* Update timestamp, to within 1 second. */
334 this.lasttime = new Date(1000 * buf.readUInt32LE(0));
335 var datalen = buf.readUInt32LE(8);
336 if (datalen > 16384) {
337 // Something is probably wrong...
338 tslog("DGL %s: looking for %d bytes", this.tag(), datalen);
339 }
340 var databuf = new Buffer(datalen);
341 fs.read(this.fd, databuf, 0, datalen, this.rpos, this.handledata.bind(this));
342 };
343
344 DglSession.prototype.handledata = function (err, n, buf) {
345 if (err || n < buf.length) {
346 /* Next time, read the header again. */
347 this.rpos -= 12;
348 this.reading = false;
349 tslog("DGL %s: expected %d bytes, got %d", this.tag(), buf.length, n);
350 return;
351 }
352 this.rpos += n;
353 this.reading = false;
354 /* Process the data */
355 this.framepush(buf);
356 var wmsg = JSON.stringify({"t": "d", "d": buf.toString("hex")});
357 for (var i = 0; i < this.watchers.length; i++) {
358 if (this.watchers[i].connected)
359 this.watchers[i].sendUTF(wmsg);
360 }
361 this.emit("data", buf);
362 /* Recurse. */
363 this.startchunk();
364 };
365
366 /* Handles events from the ttyrec file watcher. */
367 DglSession.prototype.notifier = function (ev, finame) {
368 if (ev == "change")
369 this.startchunk();
370 /* If another kind of event appears, something strange happened. */
371 };
372
373 DglSession.prototype.close = function () {
374 this.recwatcher.close();
375 /* Ensure all data is handled before quitting. */
376 this.startchunk();
377 var connlist = this.watchers;
378 this.watchers = [];
379 for (var i = 0; i < connlist.length; i++) {
380 if (connlist[i].connected)
381 connlist[i].close();
382 }
383 fs.close(this.fd);
384 this.emit("close");
385 gamemux.emit('end', this.gname, this.pname);
386 tslog("DGL %s: closed", this.tag());
387 };
379 388
380 function wsStartGame(wsReq) { 389 function wsStartGame(wsReq) {
381 var playmatch = wsReq.resourceURL.pathname.match(/^\/play\/([^\/]*)$/); 390 var playmatch = wsReq.resourceURL.pathname.match(/^\/play\/([^\/]*)$/);
382 if (!playmatch[1] || !(playmatch[1] in games)) { 391 if (!playmatch[1] || !(playmatch[1] in games)) {
383 wsReq.reject(404, errorcodes[2]); 392 wsReq.reject(404, errorcodes[2]);