Skip to content

Commit

Permalink
Sockets: Go rewrite
Browse files Browse the repository at this point in the history
Work in progress. I haven't written documentation for the Go code or
confirmed whether this works as intended on Windows yet.

This turned into a pretty substantial refactor of the Node version of
sockets-related code to not only to make using Go child processes
possible, but to optimize it and make unit testing of it and any code
dependent on it possible to write entirely synchronously. sockets.js
and sockets-workers.js are now written in Typescript, though they won't
be able to be transpiled until after Config, Users, Dnsbl, and Monitor
work with it as well.

Fixes #2943
  • Loading branch information
Morfent committed May 21, 2017
1 parent b54dfaa commit 912a397
Show file tree
Hide file tree
Showing 19 changed files with 2,312 additions and 625 deletions.
7 changes: 7 additions & 0 deletions config/config-example.js
Expand Up @@ -10,6 +10,13 @@ exports.port = 8000;
// know what you are doing.
exports.proxyip = false;

// Go language - whether or not to use Go instead of Node.js to host the static
// and SockJS servers. Go is more likely to be more performant than Node.js for
// this purpose, but this should be kept set to false unless you're capable of
// debugging any issues that may arise due to the additional complexity of
// the code needed for this to run.
exports.golang = false;

// Pokemon of the Day - put a pokemon's name here to make it Pokemon of the Day
// The PotD will always be in the #2 slot (not #1 so it won't be a lead)
// in every Random Battle team.
Expand Down
35 changes: 35 additions & 0 deletions dev-tools/sockets.js
@@ -0,0 +1,35 @@
'use strict';

const {Session, SockJSConnection} = require('sockjs/lib/transport');

const chars = 'abcdefghijklmnopqrstuvwxyz1234567890-';
let sessionidCount = 0;

/**
* @return string
*/
function generateSessionid() {
let ret = '';
let idx = sessionidCount;
for (let i = 0; i < 8; i++) {
ret = chars[idx % chars.length] + ret;
idx = idx / chars.length | 0;
}
sessionidCount++;
return ret;
}

/**
* @param {string} sessionid
* @param {{options: {{}}} config
* @return SockJSConnection
*/
exports.createSocket = function (sessionid = generateSessionid(), config = {options: {}}) {
let session = new Session(sessionid, config);
let socket = new SockJSConnection(session);
socket.remoteAddress = '127.0.0.1';
socket.protocol = 'websocket';
return socket;
};

// TODO: move worker mocks here, use require('../sockets-workers').Multiplexer to stub IPC
74 changes: 74 additions & 0 deletions pokemon-showdown
Expand Up @@ -2,6 +2,8 @@
'use strict';

const child_process = require('child_process');
const fs = require('fs');
const path = require('path');

// Make sure we're Node 6+

Expand All @@ -22,6 +24,78 @@ try {
child_process.execSync('npm install --production', {stdio: 'inherit'});
}

// Check if the server is configured to use Go, and ensure the required
// environment variables and dependencies are available if that is the case

let config;
try {
config = require('./config/config');
} catch (e) {}

if (config && config.golang) {
if (!process.env.GOPATH) {
console.log('The GOPATH environment variable is not set! It is required in order to run the server using Go.');
process.exit(0);
}
if (!process.env.GOROOT) {
console.log('The GOROOT environment variable is not set! It is required in order to run the server using Go.');
process.exit(0);
}

const dependencies = ['github.com/gorilla/mux', 'github.com/igm/sockjs-go/sockjs'];
let packages = child_process.execSync('go list all', {stdio: null, encoding: 'utf8'});
for (let dep of dependencies) {
if (!packages.includes(dep)) {
console.log(`Dependency ${dep} is not installed. Fetching...`);
child_process.execSync(`go get ${dep}`, {stdio: 'inherit'});
}
}

const {GOPATH} = process.env;
let stat;
let needsSrcDir = false;
try {
stat = fs.lstatSync(path.resolve(GOPATH, 'src/github.com/Zarel'));
} catch (e) {
needsSrcDir = true;
} finally {
if (stat && !stat.isDirectory()) {
needsSrcDir = true;
}
}

if (needsSrcDir) {
try {
fs.mkdirSync(path.resolve(GOPATH, 'src/github.com/Zarel'));
} catch (e) {
console.error(`Cannot make go source directory for the sockets library files! Symlink them manually from ${__dirname} to ${path.resolve(GOPATH, 'src/github.com/Zarel/Pokemon-Showdown/')}`);
process.exit(0);
}
}

try {
stat = fs.lstatSync(path.resolve(GOPATH, 'src/github.com/Zarel/Pokemon-Showdown'));
} catch (e) {}

if (!stat || !stat.isSymbolicLink()) {
try {
// FIXME: does this even work on Windows? Check to see if `mklink /J`
// might be needed instead
fs.symlink(__dirname, path.resolve(GOPATH, 'src/github.com/Zarel/Pokemon-Showdown'));
} catch (e) {
console.error(`Cannot make go source directory for the sockets library files! Symlink them manually from ${__dirname} to ${path.resolve(GOPATH, './src/github.com/Zarel/Pokemon-Showdown/')}`);
process.exit(0);
}
}

console.log('Building Go source libs...');
try {
child_process.execSync('go install github.com/Zarel/Pokemon-Showdown/sockets', {stdio: 'inherit'});
} catch (e) {
process.exit(0);
}
}

// Start the server. We manually load app.js so it can be configured to run as
// the main module, rather than this file being considered the main module.
// This ensures any dependencies that were just installed can be found when
Expand Down

0 comments on commit 912a397

Please sign in to comment.