Skip to content

Commit

Permalink
WIP New Replays
Browse files Browse the repository at this point in the history
New Replays isn't done yet but I've done a lot of work on it and
probably it deserves a commit.

Why rewrite Replays:

- I redesigned pokemonshowdown.com to look more modern and support
  dark mode, and rejiggering Old Replays would be a lot of work
  anyway.

- It'd be nice to actually deploy some of PS's whole Preact
  infrastructure somewhere, instead of it just being in development
  hell.

- Nice to get a second look at the relevant code, leading to fixes
  like fc00e68 and f28b1e9.

- Replays is due for a migration from JS/PHP to TS anyway.

Anyway, feel free to try out New Replays, it's live in:

https://pokemonshowdown.com/replays/

Old Replays will stick around until we hit feature parity, but
that shouldn't be too long (I know, famous last words).
  • Loading branch information
Zarel committed Oct 26, 2023
1 parent e64b9a8 commit fd28aca
Show file tree
Hide file tree
Showing 10 changed files with 818 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ package-lock.json
/website/files/
/website/images/
/website/ads.txt
/website/replays/index.html
/website/replays/js/
node_modules
38 changes: 27 additions & 11 deletions build-tools/update
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ if (process.argv[2] === 'full') {

compiledFiles += compiler.compileToDir(`src`, `js`, compileOpts);

compiledFiles += compiler.compileToDir(`src`, `js`, compileOpts);
compiledFiles += compiler.compileToDir(`website/replays/src`, `website/replays/js`, compileOpts);

compiledFiles += compiler.compileToFile(
['src/battle-dex.ts', 'src/battle-dex-data.ts', 'src/battle-log.ts', 'src/battle-log-misc.js', 'data/pokemon-showdown/server/chat-formatter.ts', 'data/text.js', 'src/battle-text-parser.ts'],
Expand All @@ -127,7 +127,7 @@ console.log(

process.stdout.write("Updating cachebuster and URLs... ");

const URL_REGEX = /(src|href)="\/(.*?)(\?[a-z0-9]*?)?"/g;
const URL_REGEX = /(src|href)="(.*?)(\?[a-z0-9]*?)?"/g;

function updateURL(a, b, c, d) {
c = c.replace('/replay.pokemonshowdown.com/', '/' + routes.replays + '/');
Expand All @@ -136,16 +136,27 @@ function updateURL(a, b, c, d) {
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 + '"';
if (c.startsWith('/')) {
let hash = Math.random(); // just in case creating the hash fails
try {
const filename = c.slice(1).replace('/' + routes.client + '/', '');
const fstr = fs.readFileSync(filename, {encoding: 'utf8'});
hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8);
} catch (e) {}

Check failure on line 146 in build-tools/update

View workflow job for this annotation

GitHub Actions / build (14.x)

Trailing spaces not allowed
return b + '="' + c + '?' + hash + '"';
} else {
// hardcoded to Replays rn; TODO: generalize
let hash;
try {
const fstr = fs.readFileSync('website/replays/' + c, {encoding: 'utf8'});
hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8);
} catch (e) {}

return b + '="' + c + '?' + (hash || 'v1') + '"';
}
} else {
return b + '="/' + c + '"';
return b + '="' + c + '"';
}
}

Expand All @@ -154,6 +165,11 @@ function writeFiles(indexContents, preactIndexContents, crossprotocolContents, r
fs.writeFileSync('preactalpha.html', preactIndexContents);
fs.writeFileSync('crossprotocol.html', crossprotocolContents);
fs.writeFileSync('js/replay-embed.js', replayEmbedContents);

let replaysContents = fs.readFileSync('website/replays/index.template.html', {encoding: 'utf8'});
replaysContents = replaysContents.replace(URL_REGEX, updateURL);
fs.writeFileSync('website/replays/index.html', replaysContents);

console.log("DONE");
}

Expand Down
3 changes: 3 additions & 0 deletions replays/battle.log.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
}

if (isset($_REQUEST['json'])) {
$matchSuccess = preg_match('/\\n\\|tier\\|([^|]*)\\n/', $replay['log'], $matches);
if ($matchSuccess) $replay['format'] = $matches[1];

header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
die(json_encode($replay));
Expand Down
2 changes: 2 additions & 0 deletions website/.htaccess
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ RewriteRule ^servers\/([A-Za-z0-9-]+)\.json$ servers/server.php?id=$1&json [L,QS
# RewriteRule ^replay\/?search/?$ replay/search.php [L,QSA]
# RewriteRule ^replay\/?([A-Za-z0-9-]+)$ replay/battle.php?name=$1 [L,QSA]

RewriteRule ^replays/([a-z0-9-]+)$ replays/index.html [L,QSA]

RewriteRule ^replay\/(.*)$ https://replay.pokemonshowdown.com/$1 [R=302,L]
RewriteRule ^dex\/(.*)$ https://dex.pokemonshowdown.com/$1 [R=302,L]
RewriteRule ^pokedex\/(.*)$ https://dex.pokemonshowdown.com/$1 [R=302,L]
Expand Down
93 changes: 93 additions & 0 deletions website/replays/index.template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />

<title>Replays - Pok&eacute;mon Showdown!</title>

<link rel="stylesheet" href="/style/global.css?" />
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/font-awesome.css?" />
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/battle.css?a7" />
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/utilichart.css?a7" />

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-26211653-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());

gtag('config', 'UA-26211653-1');
</script>
<!-- End Google Analytics -->
<!-- Venatus Ad Manager - Install in <HEAD> of page -->
<script src="https://hb.vntsm.com/v3/live/ad-manager.min.js" type="text/javascript" data-site-id="642aba63ec9a7b11c3c9c1be" data-mode="scan" async></script>
<!-- / Venatus Ad Manager -->

<style>
@media (max-width:820px) {
.battle {
margin: 0 auto;
}
.battle-log {
margin: 7px auto 0;
max-width: 640px;
height: 300px;
position: static;
}
}
.optgroup {
display: inline-block;
}
.button-first, .button-middle {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.button-last, .button-middle {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
margin-left: -1px;
}
</style>

<div>

<header>
<div class="nav-wrapper"><ul class="nav">
<li><a class="button nav-first" href="/"><img src="/images/pokemonshowdownbeta.png" srcset="/images/pokemonshowdownbeta.png 1x, /images/[email protected] 2x" alt="Pok&eacute;mon Showdown" width="146" height="44" /> Home</a></li>
<li><a class="button" href="/dex/">Pok&eacute;dex</a></li>
<li><a class="button cur" href="/replays/">Replays</a></li>
<li><a class="button" href="/ladder/">Ladder</a></li>
<li><a class="button nav-last" href="/forums/">Forum</a></li>
<li><a class="button greenbutton nav-first nav-last" href="//play.pokemonshowdown.com/">Play</a></li>
</ul></div>
</header>

<div class="main" id="main">

<noscript><section class="section">You need to enable JavaScript to use this page; sorry!</section></noscript>

</div>

</div>

<script defer nomodule src="//play.pokemonshowdown.com/js/lib/ps-polyfill.js"></script>
<script defer src="//play.pokemonshowdown.com/js/lib/preact.min.js"></script>

<script defer src="//play.pokemonshowdown.com/config/config.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/js/lib/jquery-1.11.0.min.js"></script>
<script defer src="//play.pokemonshowdown.com/js/lib/html-sanitizer-minified.js"></script>
<script defer src="//play.pokemonshowdown.com/js/battle-sound.js"></script>
<script defer src="//play.pokemonshowdown.com/js/battledata.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/data/pokedex-mini.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/data/pokedex-mini-bw.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/data/graphics.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/data/pokedex.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/data/moves.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/data/abilities.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/data/items.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/data/teambuilder-tables.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/js/battle-tooltips.js?a7"></script>
<script defer src="//play.pokemonshowdown.com/js/battle.js?a7"></script>

<script defer src="js/utils.js?"></script>
<script defer src="js/replays.js?"></script>
7 changes: 7 additions & 0 deletions website/replays/replay-api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

require '../../lib/ntbb-session.lib.php';

echo ']' . ($curuser['loggedin'] ? $curuser['userid'] : '') . ',';

echo $users->isSysop() ? '1' : '';
51 changes: 51 additions & 0 deletions website/replays/search.json.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

$username = @$_REQUEST['user'];
$username2 = @$_REQUEST['user2'];
$format = @$_REQUEST['format'];
$contains = (@$_REQUEST['contains']);
$byRating = isset($_REQUEST['rating']);
$isPrivate = isset($_REQUEST['private']);

header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');

require_once '../../replays/replays.lib.php';

$username = $Replays->toID($username);
$isPrivateAllowed = false;
if ($isPrivate) {
header('HTTP/1.1 400 Bad Request');
die('"ERROR: you cannot access private replays with the public API"');
}

$page = intval($_REQUEST['page'] ?? 0);

$replays = null;
if ($page > 25) {
die('"ERROR: page limit is 25"');
} else if ($username || $format) {
$replays = $Replays->search([
"username" => $username,
"username2" => $username2,
"format" => $format,
"byRating" => $byRating,
"isPrivate" => $isPrivate,
"page" => $page
]);
} else if ($contains) {
$replays = $Replays->fullSearch($contains, $page);
} else {
$replays = $Replays->recent();
}

if ($replays) {
foreach ($replays as &$replay) {
if ($replay['password'] ?? null) {
$replay['id'] .= '-' . $replay['password'];
}
unset($replay['password']);
}
}

echo json_encode($replays);
51 changes: 51 additions & 0 deletions website/replays/search.notjson.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

$username = @$_REQUEST['user'];
$username2 = @$_REQUEST['user2'];
$format = @$_REQUEST['format'];
$contains = (@$_REQUEST['contains']);
$byRating = isset($_REQUEST['rating']);
$isPrivate = isset($_REQUEST['private']);

header('Content-Type: application/json');

require_once '../../replays/replays.lib.php';
include_once '../../lib/ntbb-session.lib.php';

$username = $users->userid($username);
$isPrivateAllowed = ($username === $curuser['userid'] || $users->isSysop());
if ($isPrivate && !$isPrivateAllowed) {
header('HTTP/1.1 403 Forbidden');
die('"ERROR: access denied"');
}

$page = intval($_REQUEST['page'] ?? 0);

$replays = null;
if ($page > 25) {
die('"ERROR: page limit is 25"');
} else if ($username || $format) {
$replays = $Replays->search([
"username" => $username,
"username2" => $username2,
"format" => $format,
"byRating" => $byRating,
"isPrivate" => $isPrivate,
"page" => $page
]);
} else if ($contains) {
$replays = $Replays->fullSearch($contains, $page);
} else {
$replays = $Replays->recent();
}

if ($replays) {
foreach ($replays as &$replay) {
if ($replay['password'] ?? null) {
$replay['id'] .= '-' . $replay['password'];
}
unset($replay['password']);
}
}

echo ']' . json_encode($replays);
Loading

0 comments on commit fd28aca

Please sign in to comment.