]>
Commit | Line | Data |
---|---|---|
c7b4bd19 BB |
1 | 'use strict'; |
2 | ||
3 | const Net = require('net'); | |
4 | const EventEmitter = require('events'); | |
5 | ||
6 | const Util = require('./util'); | |
7 | ||
8 | // Interpret as Command Sequences. | |
9 | const kEscape = new Buffer([0xFF, 0xF4, 0XFF, 0xFD, 0x06]); // IAC IP IAC DO TIMING_MARK | |
10 | const kNAWSRequest = new Buffer([0xFF, 0xFD, 0X1F]); // IAC DO NAWS | |
11 | const kNAWSResponse = new Buffer([0xFF, 0xFB, 0X1F, 0xFF, 0xFA, 0X1F]); // IAC WILL NAWS IAC SB NAWS | |
12 | ||
13 | // Main tomato sauce class. Properties: | |
14 | // * screens <TomatoSauce.IScreen[]> | |
15 | // * renderers <TomatoSauce.IRenderer[]> | |
16 | // * port <int> | |
17 | // * frequency <int> | |
18 | // * modulation <int> | |
19 | // | |
20 | // The main entry point is the #run() function. | |
21 | // | |
22 | // It emits a listening event that contains the server information on | |
23 | // the `server` key inside the `data` property of the event. | |
24 | // | |
25 | // It also emits an error event that contains the error information on | |
26 | // the `error` key inside the `data` property of the event. | |
27 | const TomatoSauce = class TomatoSauce extends EventEmitter { | |
28 | ||
29 | constructor (config) { | |
30 | super(); | |
31 | ||
32 | this.screens = []; | |
33 | this.renderers = []; | |
34 | ||
35 | Object.assign(this, config || {}); | |
36 | } | |
37 | ||
38 | // Here's where things get started. | |
39 | run () { | |
40 | this._startServer(); | |
41 | this._bindEvents(); | |
42 | } | |
43 | ||
44 | // Creates a socket, server based on the configuration. Emits the | |
45 | // listening event when ready. | |
46 | _startServer () { | |
47 | const server = Net.createServer(); | |
48 | this.server = server; | |
49 | server.listen(this.port, function () { | |
50 | this.emit('listening', { | |
51 | data: { | |
52 | server: server | |
53 | } | |
54 | }); | |
55 | }.bind(this)); | |
56 | } | |
57 | ||
58 | _bindEvents () { | |
59 | // Send the error event all the way up. | |
60 | this.server.on('error', function (err) { | |
61 | this.emit('error', { | |
62 | data: { | |
63 | error: err | |
64 | } | |
65 | }); | |
66 | }.bind(this)); | |
67 | ||
68 | // Send the error event all the way up. | |
69 | this.server.on('connection', function (socket) { | |
70 | this._renderScreen(socket); | |
71 | }.bind(this)); | |
72 | } | |
73 | ||
74 | // Obtains viewport size, and initializes a random screen with a random | |
75 | // renderer, setting the required interval to draw. | |
76 | _renderScreen (socket) { | |
77 | let connectionData = { | |
78 | width: null, | |
79 | height: null, | |
80 | modulation: 0, | |
81 | screen: this._getScreen(), | |
82 | renderer: this._getRenderer(), | |
83 | socket: socket | |
84 | }; | |
85 | let interval = null; | |
86 | ||
87 | socket.write(kNAWSRequest); | |
88 | ||
89 | socket.on('data', function (data) { | |
90 | if (data.slice(0, 6).compare(kNAWSResponse) === 0) { | |
91 | connectionData.width = Util.parse16BitBuffer(data.slice(6, 8)); | |
92 | connectionData.height = Util.parse16BitBuffer(data.slice(8, 10)); | |
93 | ||
94 | socket.write('\x1B[2J'); // Clear the Screen (CSI 2 J) | |
95 | interval = setInterval(this._writeMessage.bind(this, connectionData), this.frequency); | |
96 | } | |
97 | ||
98 | if (data.compare(kEscape) === 0) { | |
99 | socket.write('\n'); | |
100 | clearInterval(interval); | |
101 | socket.end(); | |
102 | } | |
103 | }.bind(this)); | |
104 | } | |
105 | ||
106 | // Resets the cursor, gets a frame and sends it to the socket. | |
107 | _writeMessage (connectionData) { | |
108 | let payload = connectionData.screen(connectionData.modulation, connectionData.width, connectionData.height, connectionData.renderer); | |
109 | let message = `\x1B[1;1H${payload}`; // reset cursor position before payload | |
110 | ||
111 | connectionData.modulation = (connectionData.modulation + this.modulation) % 256; | |
112 | connectionData.socket.write(message); | |
113 | } | |
114 | ||
115 | _getScreen () { | |
116 | return Util.pickRandom(this.screens); | |
117 | } | |
118 | ||
119 | _getRenderer () { | |
120 | return Util.pickRandom(this.renderers); | |
121 | } | |
122 | }; | |
123 | ||
124 | // Defaults. | |
125 | TomatoSauce.prototype.port = 9999; | |
126 | TomatoSauce.prototype.frequency = 333; | |
127 | TomatoSauce.prototype.modulation = 5; | |
128 | ||
129 | module.exports = TomatoSauce; |