/* socket.js, a simple wrapper for a YWOT websocket connection */ const ws = require('ws') const EventEmitter = require('events'); class retryws extends EventEmitter{ // a wrapper on ws that retries on failure constructor(addr) { super(); let sock; let sockdown = true; this.send = (message) => { if (sockdown){ throw "Socket is down!"; } sock.send(message); } let self = this; function sockinit(){ // addr is not expected to change, so not handed as input; similarly, sock is not passed in a chain because behavior is expected to be serial (either initializing or retrying) sock = new ws(addr); sock.on('open', () => { sockdown = false; self.emit('open'); }); sock.on('close', (err) => { console.log(err); setTimeout(sockinit,1000); sockdown = true; self.emit('close'); }); sock.on('error', (err) => { console.log(err); sock.close(); }); sock.on('message', (message) => { self.emit('message',message);}); } sockinit(); } } class Socket extends retryws { constructor(world='') { // Takes the name of the world, which can be an empty string. let loc = (world == '') ? '' : `${world}/`; let addr = `wss://www.yourworldoftext.com/${loc}ws/`; super(addr); this.on('message', (msg)=>{ msg = JSON.parse(msg); switch (msg.kind){ case 'write': this.emit('write', msg.accepted, msg.rejected); break; // A confirmation message for writes (accepted/rejected updates) case 'cursor': this.emit('cursor', msg.positions.map(pos => [pos.tileY*8+pos.charY,pos.tileX*16+pos.charX]), msg.sender); break; // Must be used for self-identification case 'tileUpdate': this.emit('tileUpdate', msg.sender, msg.source, msg.tiles); break; // Any change by another user or possibly oneself case 'fetch': this.emit('fetch', msg.tiles); break; // The response to a fetch request } }); this.fetch = function(coords){ //coords is a list of quadruplets, each a min/max pair of y/x coordinate pairs which describes at most 1000 tiles //Unchecked for speed for (var i=0; i 200){ throw "Too many characters to write"; } for (var i=0; i