Skip to content

Commit

Permalink
Sockets: Go rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
Morfent committed Nov 3, 2017
1 parent 5b9e1f3 commit 131bf27
Show file tree
Hide file tree
Showing 21 changed files with 2,696 additions and 729 deletions.
10 changes: 9 additions & 1 deletion config/config-example.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,22 @@ exports.workers = 1;
// TODO: allow SSL to actually be possible to use for third-party servers at
// some point.

// golang - toggle using Go instead of Node for sockets workers
// Node workers are more unstable at handling connections because of bugs in
// sockjs-node, but sending/receiving messages over connections on Go workers
// is slightly slower due to the extra work involved in performing IPC with
// them safely. This should be left set to false unless you know what you are
// doing.
exports.golang = false;

// proxyip - proxy IPs with trusted X-Forwarded-For headers
// This can be either false (meaning not to trust any proxies) or an array
// of strings. Each string should be either an IP address or a subnet given
// in CIDR notation. You should usually leave this as `false` unless you
// know what you are doing.
exports.proxyip = false;

// ofe - write heapdumps if sockets.js workers run out of memory.
// ofe - write heapdumps if Node sockets workers run out of memory
// If you wish to enable this, you will need to install ofe, as it is not a
// installed by default:
// $ npm install --no-save ofe
Expand Down
35 changes: 35 additions & 0 deletions dev-tools/sockets.js
Original file line number Diff line number Diff line change
@@ -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
65 changes: 51 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@
"private": true,
"license": "MIT",
"devDependencies": {
"@types/cloud-env": "^0.2.0",
"@types/node": "^8.0.28",
"@types/node-static": "^0.7.0",
"@types/nodemailer": "^1.3.33",
"@types/ofe": "^0.5.0",
"@types/sockjs": "^0.3.31",
"eslint": "^4.0.0",
"mocha": "^3.0.0",
"@types/node": "^8.0.1",
"@types/nodemailer": "^1.3.33",
"typescript": "^2.5.0-dev.20170622"
}
}
82 changes: 82 additions & 0 deletions pokemon-showdown
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,88 @@ try {
}

if (!process.argv[2] || /^[0-9]+$/.test(process.argv[2])) {
// 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) {
// GOPATH and GOROOT are optional to a degree, but we need them in order
// to be able to handle Go dependencies. Since Go only cares about the
// first path in the list, so will we.
const GOPATH = child_process.execSync('go env GOPATH', {stdio: null, encoding: 'utf8'})
.trim()
.split(path.delimiter)[0]
.replace(/^"(.*)"$/, '$1');
if (!GOPATH) {
// Should never happen, but it does on Bash on Ubuntu on Windows.
console.error('There is no $GOPATH environment variable set.');
process.exit(1);
}

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(`Installing ${dep}...`);
child_process.execSync(`go install ${dep}`, {stdio: 'inherit'});
}
}

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;
}
}

let srcPath = path.resolve(process.cwd(), 'sockets');
let tarPath = path.resolve(GOPATH, 'src/github.com/Zarel/Pokemon-Showdown/sockets');
if (needsSrcDir) {
try {
fs.mkdirSync(path.resolve(GOPATH, 'src/github.com/Zarel'));
fs.mkdirSync(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 ${srcPath} to ${tarPath}`);
process.exit(1);
}
}

try {
stat = fs.lstatSync(tarPath);
} catch (e) {}

if (!stat || !stat.isSymbolicLink()) {
// Windows requires administrator privileges to make symlinks, so we
// make junctions instead. For our purposes they're compatible enough
// with symlinks on UNIX-like OSes.
let symlinkType = (process.platform === 'win32') ? 'junction' : 'dir';
try {
fs.symlinkSync(srcPath, tarPath, symlinkType);
} catch (e) {
console.error(`Cannot make go source directory for the sockets library files! Symlink them manually from ${srcPath} to ${tarPath}`);
process.exit(1);
}
}

console.log('Building Go source libs...');
try {
child_process.execSync('go install github.com/Zarel/Pokemon-Showdown/sockets', {stdio: 'inherit'});
} catch (e) {
// Go will show the errors that caused compiling Go's files to fail, so
// there's no reason to bother logging anything of our own.
process.exit(1);
}
}

// 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
Loading

0 comments on commit 131bf27

Please sign in to comment.