aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHolden Rohrer <holden.rohrer@gmail.com>2019-12-17 20:09:34 -0500
committerHolden Rohrer <holden.rohrer@gmail.com>2019-12-17 20:09:34 -0500
commitd362969d840db3b2e157655c1b2a6325fec2aacc (patch)
treecd88f6aa6b436b1bbdb993eb2124b124ed1546e1
parentbc8fcae59961fae2702c919daba4e3ca074a9fc9 (diff)
finished world wrapper
-rw-r--r--socket.js67
1 files changed, 67 insertions, 0 deletions
diff --git a/socket.js b/socket.js
new file mode 100644
index 0000000..4a030f5
--- /dev/null
+++ b/socket.js
@@ -0,0 +1,67 @@
+/* socket.js, a simple wrapper for a YWOT websocket connection */
+
+class retryws extends EventEmitter{ // a wrapper on ws that retries on failure
+ constructor(addr) {
+ let sock;
+ this.sockdown = true;
+ this.send = (message) => {
+ if (sockdown){
+ throw "Socket is down!";
+ }
+ sock.send(message);
+ }
+ 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', () => { this.emit('open'); sockdown = false; });
+ sock.on('close', (err) => { setTimeout(sockinit,1000); sockdown = true; this.emit('close'); });
+ sock.on('error', (err) => { sock.close(); });
+ sock.on('message', (message) => { this.emit('message',message); });
+ }
+ }
+}
+
+
+class Socket extends retryws {
+ constructor(world='') { // Takes the name of the world, which can be an empty string.
+ let loc = (name = '') ? '' : `/${name}`;
+ let addr = `wss://www.yourworldoftext.com/${loc}ws`;
+ super(addr);
+ this.on('message', (msg)=>{
+ msg = JSON.parse(msg);
+ switch (message.kind){
+ case 'write':
+ this.emit('write', message.accepted, message.rejected); break; // A confirmation message for writes (accepted/rejected updates)
+ case 'cursor':
+ this.emit('cursor', message.positions, message.sender); break; // Must be used for self-identification
+ case 'tileUpdate':
+ this.emit('tileUpdate', message.sender, message.source, message.tiles); break; // Any change by another user or possibly oneself
+ case 'fetch':
+ this.emit('fetch', message.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<coords.length; i++){
+ coords[i] = {"minY":coords[i][0], "minX":coords[i][1], "maxY":coords[i][2], "maxX":coords[i][3]};
+ }
+ this.send(`{"fetchRectangles":${JSON.stringify(coords)},"kind":"fetch","v":"3"}`);
+ }
+
+ this.write = function(chars){ //chars is an list of triplets [ tile coordinate (y/x), pixel coordinate (y/x < 16), char ]
+ if (chars.length > 200){
+ throw "Too many characters to write";
+ }
+ for (var i=0; i<chars.length; i++){
+ chars[i].splice(4,0,0); //
+ chars[i].push(i);
+ }
+ this.send(`{"edits":${JSON.stringify(chars)},"kind":"write"}`;
+ }
+
+ this.cursor = function(coords){ //coords is just one quadruplet analagous to fetch; I think the api could handle more, but it's unnecessary for now.
+ this.send(`"kind":"cursor","positions":[${JSON.stringify(coords)}]}`;
+ }
+
+ }
+}