diff --git a/.gitignore b/.gitignore index 4efba1ddb..a8e9bbcb1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /index.php /index.html /preactalpha.html +/crossprotocol.html /data/* node_modules/ eslint-cache/ @@ -39,6 +40,7 @@ package-lock.json /js/panel-teambuilder-team.js /js/panel-teamdropdown.js /js/panel-battle.js +/js/replay-embed.js /replays/caches/ /replays/replay-config.inc.php diff --git a/action.php b/action.php index 22e4b0150..4dc5d90fd 100644 --- a/action.php +++ b/action.php @@ -9,9 +9,11 @@ License: GPLv2 or later error_reporting(E_ALL); +include_once __DIR__ . '/config/config.inc.php'; + if (@$_GET['act'] === 'dlteam') { header("Content-Type: text/plain; charset=utf-8"); - if (substr(@$_SERVER['HTTP_REFERER'], 0, 32) !== 'https://play.pokemonshowdown.com') { + if (substr(@$_SERVER['HTTP_REFERER'], 0, 32) !== 'https://' . $psconfig['routes']['client']) { // since this is only to support Chrome on HTTPS, we can get away with a very specific referer check die("access denied"); } @@ -26,9 +28,9 @@ if (preg_match('/^http\\:\\/\\/[a-z0-9]+\\.psim\\.us\\//', $_SERVER['HTTP_REFERE } // header("X-Debug: " . @$_SERVER['HTTP_REFERER']); -include_once 'lib/ntbb-session.lib.php'; -include_once '../pokemonshowdown.com/config/servers.inc.php'; -include_once 'lib/dispatcher.lib.php'; +require_once __DIR__ . '/lib/ntbb-session.lib.php'; +include_once __DIR__ . '/../pokemonshowdown.com/config/servers.inc.php'; +include_once __DIR__ . '/lib/dispatcher.lib.php'; $dispatcher = new ActionDispatcher(array( new DefaultActionHandler(), diff --git a/build-tools/build-indexes b/build-tools/build-indexes index 4ff25d89d..e58069041 100755 --- a/build-tools/build-indexes +++ b/build-tools/build-indexes @@ -8,18 +8,18 @@ const child_process = require("child_process"); const rootDir = path.resolve(__dirname, '..'); process.chdir(rootDir); -if (!fs.existsSync('data/Pokemon-Showdown')) { +if (!fs.existsSync('data/pokemon-showdown')) { child_process.execSync('git clone https://github.com/smogon/pokemon-showdown.git', { cwd: 'data', }); } process.stdout.write("Syncing data from Git repository... "); -child_process.execSync('git pull', {cwd: 'data/Pokemon-Showdown'}); -child_process.execSync('npm run build', {cwd: 'data/Pokemon-Showdown'}); +child_process.execSync('git pull', {cwd: 'data/pokemon-showdown'}); +child_process.execSync('npm run build', {cwd: 'data/pokemon-showdown'}); console.log("DONE"); -const Dex = require('../data/Pokemon-Showdown/.sim-dist/dex').Dex; +const Dex = require('../data/pokemon-showdown/.sim-dist/dex').Dex; const toID = Dex.getId; process.stdout.write("Loading gen 6 data... "); Dex.includeData(); @@ -985,7 +985,7 @@ console.log("DONE"); process.stdout.write("Building `data/pokedex.js`... "); { - const Pokedex = requireNoCache('../data/Pokemon-Showdown/.data-dist/pokedex.js').BattlePokedex; + const Pokedex = requireNoCache('../data/pokemon-showdown/.data-dist/pokedex.js').BattlePokedex; for (const id in Pokedex) { const entry = Pokedex[id]; if (Dex.data.FormatsData[id]) { @@ -1011,7 +1011,7 @@ console.log("DONE"); process.stdout.write("Building `data/moves,items,abilities,typechart,learnsets.js`..."); { - const Movedex = requireNoCache('../data/Pokemon-Showdown/.data-dist/moves.js').BattleMovedex; + const Movedex = requireNoCache('../data/pokemon-showdown/.data-dist/moves.js').BattleMovedex; const buf = 'exports.BattleMovedex = ' + es3stringify(Movedex) + ';'; fs.writeFileSync('data/moves.js', buf); } @@ -1021,7 +1021,7 @@ process.stdout.write("Building `data/moves,items,abilities,typechart,learnsets.j *********************************************************/ { - const Items = requireNoCache('../data/Pokemon-Showdown/.data-dist/items.js').BattleItems; + const Items = requireNoCache('../data/pokemon-showdown/.data-dist/items.js').BattleItems; const buf = 'exports.BattleItems = ' + es3stringify(Items) + ';'; fs.writeFileSync('data/items.js', buf); } @@ -1031,7 +1031,7 @@ process.stdout.write("Building `data/moves,items,abilities,typechart,learnsets.j *********************************************************/ { - const Abilities = requireNoCache('../data/Pokemon-Showdown/.data-dist/abilities.js').BattleAbilities; + const Abilities = requireNoCache('../data/pokemon-showdown/.data-dist/abilities.js').BattleAbilities; const buf = 'exports.BattleAbilities = ' + es3stringify(Abilities) + ';'; fs.writeFileSync('data/abilities.js', buf); } @@ -1041,7 +1041,7 @@ process.stdout.write("Building `data/moves,items,abilities,typechart,learnsets.j *********************************************************/ { - const TypeChart = requireNoCache('../data/Pokemon-Showdown/.data-dist/typechart.js').BattleTypeChart; + const TypeChart = requireNoCache('../data/pokemon-showdown/.data-dist/typechart.js').BattleTypeChart; const buf = 'exports.BattleTypeChart = ' + es3stringify(TypeChart) + ';'; fs.writeFileSync('data/typechart.js', buf); } @@ -1051,7 +1051,7 @@ process.stdout.write("Building `data/moves,items,abilities,typechart,learnsets.j *********************************************************/ { - const Aliases = requireNoCache('../data/Pokemon-Showdown/.data-dist/aliases.js').BattleAliases; + const Aliases = requireNoCache('../data/pokemon-showdown/.data-dist/aliases.js').BattleAliases; const buf = 'exports.BattleAliases = ' + es3stringify(Aliases) + ';'; fs.writeFileSync('data/aliases.js', buf); } @@ -1061,7 +1061,7 @@ process.stdout.write("Building `data/moves,items,abilities,typechart,learnsets.j *********************************************************/ { - const FormatsData = requireNoCache('../data/Pokemon-Showdown/.data-dist/formats-data.js').BattleFormatsData; + const FormatsData = requireNoCache('../data/pokemon-showdown/.data-dist/formats-data.js').BattleFormatsData; const buf = 'exports.BattleFormatsData = ' + es3stringify(FormatsData) + ';'; fs.writeFileSync('data/formats-data.js', buf); } @@ -1071,7 +1071,7 @@ process.stdout.write("Building `data/moves,items,abilities,typechart,learnsets.j *********************************************************/ { - const Formats = requireNoCache('../data/Pokemon-Showdown/.config-dist/formats.js').Formats; + const Formats = requireNoCache('../data/pokemon-showdown/.config-dist/formats.js').Formats; const buf = 'exports.Formats = ' + es3stringify(Formats) + ';'; fs.writeFileSync('data/formats.js', buf); } @@ -1081,7 +1081,7 @@ process.stdout.write("Building `data/moves,items,abilities,typechart,learnsets.j *********************************************************/ { - const Learnsets = requireNoCache('../data/Pokemon-Showdown/.data-dist/learnsets.js').BattleLearnsets; + const Learnsets = requireNoCache('../data/pokemon-showdown/.data-dist/learnsets.js').BattleLearnsets; const buf = 'exports.BattleLearnsets = ' + es3stringify(Learnsets) + ';'; fs.writeFileSync('data/learnsets.js', buf); } diff --git a/build-tools/build-learnsets b/build-tools/build-learnsets index c6ecc41b3..2fb7fbcc7 100755 --- a/build-tools/build-learnsets +++ b/build-tools/build-learnsets @@ -18,7 +18,7 @@ const thisFile = __filename; const thisDir = __dirname; const rootDir = path.resolve(thisDir, '..'); -const Dex = require('../data/Pokemon-Showdown/.sim-dist/dex').Dex; +const Dex = require('../data/pokemon-showdown/.sim-dist/dex').Dex; const toID = Dex.getId; function updateLearnsets(callback) { diff --git a/build-tools/build-minidex b/build-tools/build-minidex index 9b9449fac..c52729223 100755 --- a/build-tools/build-minidex +++ b/build-tools/build-minidex @@ -6,7 +6,7 @@ const path = require("path"); process.chdir(path.resolve(__dirname, '..')); const imageSize = require('image-size'); -const Dex = require('./../data/Pokemon-Showdown/.sim-dist/dex').Dex; +const Dex = require('./../data/pokemon-showdown/.sim-dist/dex').Dex; const toID = Dex.getId; process.stdout.write("Updating animated sprite dimensions... "); diff --git a/build-tools/update b/build-tools/update index a92e2e3af..5cb049cce 100755 --- a/build-tools/update +++ b/build-tools/update @@ -17,6 +17,13 @@ const thisDir = __dirname; const rootDir = path.resolve(thisDir, '..'); process.chdir(rootDir); +const AUTOCONFIG_START = '/*** Begin automatically generated configuration ***/'; +const AUTOCONFIG_END = '/*** End automatically generated configuration ***/'; + +function escapeRegex(string) { + return string.replace(/[\/\*\.]/g, '\\$&'); +} + /********************************************************* * Update version number *********************************************************/ @@ -36,9 +43,27 @@ try { version += ` (${head.slice(0, 8)}${head !== origin ? `/${origin.slice(0, 8)}` : ''})`; } catch (e) {} -let configBuf = fs.readFileSync('config/config.js', {encoding: 'utf8'}); -configBuf = configBuf.replace(/\/\* version \*\/[^;\n]*;/, `/* version */ Config.version = ${JSON.stringify(version)};`); +const routes = JSON.parse(fs.readFileSync('config/routes.json')); +const autoconfigRegex = new RegExp(`${escapeRegex(AUTOCONFIG_START)}[^]+${escapeRegex(AUTOCONFIG_END)}`); +const autoconfig = `${AUTOCONFIG_START} +Config.version = ${JSON.stringify(version)}; +Config.routes = { + root: '${routes.root}', + client: '${routes.client}', + dex: '${routes.dex}', + replays: '${routes.replays}', + users: '${routes.users}', +}; +${AUTOCONFIG_END}`; + +// remove old automatically generated configuration and add the new one +let configBuf = fs.readFileSync('config/config.js', {encoding: 'utf8'}); +if (autoconfigRegex.test(configBuf)) { + configBuf = configBuf.replace(autoconfigRegex, autoconfig); +} else { + configBuf += autoconfig; +} fs.writeFileSync('config/config.js', configBuf); console.log("DONE"); @@ -82,31 +107,50 @@ if (!ignoreGraphics) { * Update cachebuster and News *********************************************************/ -function updateIndex() { - // add hashes to js and css files - process.stdout.write("Updating hashes... "); +const URL_REGEX = /(src|href)="\/(.*?)(\?[a-z0-9]*?)?"/g; + +function updateURL(a, b, c, d) { + c = c.replace('/replay.pokemonshowdown.com/', '/' + routes.replays + '/'); + c = c.replace('/dex.pokemonshowdown.com/', '/' + routes.dex + '/'); + c = c.replace('/play.pokemonshowdown.com/', '/' + routes.client + '/'); + c = c.replace('/pokemonshowdown.com/', '/' + routes.root + '/'); + + if (d) { + let hash = Math.random(); // just in case creating the hash fails + try { + const filename = c.replace('/' + routes.client + '/', ''); + const fstr = fs.readFileSync(filename, {encoding: 'utf8'}); + hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8); + } catch (e) {} + + return b + '="/' + c + '?' + hash + '"'; + } else { + return b + '="/' + c + '"'; + } +} + +function writeFiles(indexContents, preactIndexContents, crossprotocolContents, replayEmbedContents) { + process.stdout.write("Writing new HTML files... "); + fs.writeFileSync('index.html', indexContents); + fs.writeFileSync('preactalpha.html', preactIndexContents); + fs.writeFileSync('crossprotocol.html', crossprotocolContents); + console.log("DONE"); + process.stdout.write("Writing replay-embed.js... "); + fs.writeFileSync('js/replay-embed.js', replayEmbedContents); + console.log("DONE"); +} + +function updateFiles() { + // add hashes to js and css files and rewrite URLs + process.stdout.write("Updating hashes and URLs... "); let indexContents = fs.readFileSync('index.template.html', {encoding: 'utf8'}); - indexContents = indexContents.replace(/(src|href)="\/(.*?)\?[a-z0-9]*?"/g, function (a, b, c) { - let hash = Math.random(); // just in case creating the hash fails - try { - const filename = c.replace('/play.pokemonshowdown.com/', ''); - const fstr = fs.readFileSync(filename, {encoding: 'utf8'}); - hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8); - } catch (e) {} - - return b + '="/' + c + '?' + hash + '"'; - }); - let indexContents2 = fs.readFileSync('preactalpha.template.html', {encoding: 'utf8'}); - indexContents2 = indexContents2.replace(/(src|href)="\/(.*?)\?[a-z0-9]*?"/g, function (a, b, c) { - let hash = Math.random(); // just in case creating the hash fails - try { - const filename = c.replace('/play.pokemonshowdown.com/', ''); - const fstr = fs.readFileSync(filename, {encoding: 'utf8'}); - hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8); - } catch (e) {} - - return b + '="/' + c + '?' + hash + '"'; - }); + indexContents = indexContents.replace(URL_REGEX, updateURL); + let preactIndexContents = fs.readFileSync('preactalpha.template.html', {encoding: 'utf8'}); + preactIndexContents = preactIndexContents.replace(URL_REGEX, updateURL); + let crossprotocolContents = fs.readFileSync('crossprotocol.template.html', {encoding: 'utf8'}); + crossprotocolContents = crossprotocolContents.replace(URL_REGEX, updateURL); + let replayEmbedContents = fs.readFileSync('js/replay-embed.template.js', {encoding: 'utf8'}); + replayEmbedContents = replayEmbedContents.replace(/play\.pokemonshowdown\.com/g, routes.client); console.log("DONE"); // add news, only if it's actually likely to exist @@ -128,12 +172,11 @@ function updateIndex() { indexContents = indexContents.replace(//g, newsData[1]); console.log("DONE"); - process.stdout.write("Writing new `index.html` file... "); - fs.writeFileSync('index.html', indexContents); - fs.writeFileSync('preactalpha.html', indexContents2); - console.log("DONE"); + writeFiles(indexContents, preactIndexContents, crossprotocolContents, replayEmbedContents); }); + } else { + writeFiles(indexContents, preactIndexContents, crossprotocolContents, replayEmbedContents); } } -updateIndex(); +updateFiles(); diff --git a/config/config-example.inc.php b/config/config-example.inc.php index 3c0660c99..408f40e57 100644 --- a/config/config-example.inc.php +++ b/config/config-example.inc.php @@ -2,6 +2,8 @@ mb_internal_encoding('UTF-8'); +$routes = json_decode(file_get_contents(__DIR__ . '/routes.json'), true); + $psconfig = [ 'sysops' => ['zarel'], @@ -22,6 +24,9 @@ $psconfig = [ 'prefix' => 'ps_', 'charset' => 'utf8', +// routes + 'routes' => $routes, + // CORS requests 'cors' => [ diff --git a/config/config-example.js b/config/config-example.js index 6d4c58efd..22aee1d73 100644 --- a/config/config-example.js +++ b/config/config-example.js @@ -117,6 +117,17 @@ Config.whitelist = [ '4cdn\\.org' ]; +// `defaultserver` specifies the server to use when the domain name in the +// address bar is `Config.routes.client`. +Config.defaultserver = { + id: 'showdown', + host: 'sim3.psim.us', + port: 443, + httpport: 8000, + altport: 80, + registered: true +}; + Config.roomsFirstOpenScript = function () { }; diff --git a/config/routes.json b/config/routes.json new file mode 100644 index 000000000..69553f29a --- /dev/null +++ b/config/routes.json @@ -0,0 +1,7 @@ +{ + "root": "pokemonshowdown.com", + "client": "play.pokemonshowdown.com", + "dex": "dex.pokemonshowdown.com", + "replays": "replay.pokemonshowdown.com", + "users": "pokemonshowdown.com/users" +} diff --git a/crossdomain.php b/crossdomain.php index c336c0c89..ebc462995 100644 --- a/crossdomain.php +++ b/crossdomain.php @@ -1,12 +1,14 @@ ; var config = ; var yourOrigin = ; -var myOrigin = 'https://play.pokemonshowdown.com'; +var myOrigin = 'https://'; function postReply (message) { if (window.parent.postMessage === postReply) return; diff --git a/crossprotocol.html b/crossprotocol.template.html similarity index 100% rename from crossprotocol.html rename to crossprotocol.template.html diff --git a/customcss.php b/customcss.php index d0c95571e..e6fc53f20 100644 --- a/customcss.php +++ b/customcss.php @@ -2,7 +2,8 @@ ini_set('max_execution_time', 60); // 1 minute -include '../pokemonshowdown.com/config/servers.inc.php'; +require_once __DIR__ . '/../pokemonshowdown.com/config/servers.inc.php'; +require_once __DIR__ . '/config/config.inc.php'; spl_autoload_register(function ($class) { require_once('lib/css-sanitizer/'.str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php'); @@ -70,11 +71,11 @@ if ($curlret) { // Parse a stylesheet from a string $parser = Parser::newFromString($curlret); $stylesheet = $parser->parseStylesheet(); - + // Apply sanitization to the stylehseet $sanitizer = StylesheetSanitizer::newDefault(); $newStylesheet = $sanitizer->sanitize( $stylesheet ); - + // Convert the sanitized stylesheet back to text $outputcss = Wikimedia\CSS\Util::stringify( $newStylesheet, [ 'minify' => true ] ); @@ -98,7 +99,7 @@ if ($invalidate) { Done: was reloaded.

- Back to server management + Back to server management

- + @@ -115,7 +115,6 @@ ga('send', 'pageview'); LM.innerHTML += ' DONE
Loading data...'; - diff --git a/js/client-battle.js b/js/client-battle.js index 9d485d424..17ce8d7d3 100644 --- a/js/client-battle.js +++ b/js/client-battle.js @@ -280,7 +280,7 @@ if (this.battle.ended) { - var replayDownloadButton = ' Download replay

'; + var replayDownloadButton = ' Download replay

'; // battle has ended if (this.side) { diff --git a/js/client-chat.js b/js/client-chat.js index 908c4776d..3c1f3d3c6 100644 --- a/js/client-chat.js +++ b/js/client-chat.js @@ -912,7 +912,7 @@ var userid = toID(targets[0]); var registered = app.user.get('registered'); if (registered && registered.userid === userid) { - buffer += 'Reset W/L'; + buffer += 'Reset W/L'; } buffer += ''; self.add('|raw|' + buffer); diff --git a/js/client-ladder.js b/js/client-ladder.js index 2824de43a..077108cbb 100644 --- a/js/client-ladder.js +++ b/js/client-ladder.js @@ -112,7 +112,7 @@ leave: function () {}, update: function () { if (!this.curFormat) { - var buf = '

See a user\'s ranking with User lookup

' + + var buf = '

See a user\'s ranking with User lookup

' + //'

I\'m really really sorry, but as a warning: we\'re going to reset the ladder again soon to fix some more ladder bugs.

' + '

(btw if you couldn\'t tell the ladder screens aren\'t done yet; they\'ll look nicer than this once I\'m done.)

' + '