Deploy New Replays

The idea is to eventually move all client parts to their domain name
subdirectory, for clarity and better organization. New Replays is
just first.

Anyway, yeah, minor updates to New Replays, but otherwise it's just
getting deployed as-is, straight to

https://replay.pokemonshowdown.com/

The old URLs are getting taken down; they were only used for
development anyway.
This commit is contained in:
Guangcong Luo 2023-11-05 22:27:40 +00:00
parent befbf585e6
commit 7a2b323eaf
40 changed files with 244 additions and 43 deletions

14
.gitignore vendored
View File

@ -13,6 +13,7 @@ Thumbs.db
npm-debug.log
package-lock.json
/vendor/
node_modules
/js/server/
/js/*.js.map
@ -23,15 +24,14 @@ package-lock.json
/js/client-core.js
/js/client-connection.js
/replays/caches/
/replays/replay-config.inc.php
/replays/theme/wrapper.inc.php
/website/.well-known/
/website/.pages-cached/
/website/files/
/website/images/
/website/ads.txt
/website/replays/index.html
/website/replays/js/
node_modules
/replay.pokemonshowdown.com/index.html
/replay.pokemonshowdown.com/js/
/replay.pokemonshowdown.com/caches/
/replay.pokemonshowdown.com/replay-config.inc.php
/replay.pokemonshowdown.com/theme/wrapper.inc.php

View File

@ -100,7 +100,7 @@ if (process.argv[2] === 'full') {
compiledFiles += compiler.compileToDir(`src`, `js`, compileOpts);
compiledFiles += compiler.compileToDir(`website/replays/src`, `website/replays/js`, compileOpts);
compiledFiles += compiler.compileToDir(`replay.pokemonshowdown.com/src`, `replay.pokemonshowdown.com/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'],
@ -149,7 +149,7 @@ function updateURL(a, b, c, d) {
// hardcoded to Replays rn; TODO: generalize
let hash;
try {
const fstr = fs.readFileSync('website/replays/' + c, {encoding: 'utf8'});
const fstr = fs.readFileSync('replay.pokemonshowdown.com/' + c, {encoding: 'utf8'});
hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8);
} catch (e) {}
@ -166,9 +166,9 @@ function writeFiles(indexContents, preactIndexContents, crossprotocolContents, r
fs.writeFileSync('crossprotocol.html', crossprotocolContents);
fs.writeFileSync('js/replay-embed.js', replayEmbedContents);
let replaysContents = fs.readFileSync('website/replays/index.template.html', {encoding: 'utf8'});
let replaysContents = fs.readFileSync('replay.pokemonshowdown.com/index.template.html', {encoding: 'utf8'});
replaysContents = replaysContents.replace(URL_REGEX, updateURL);
fs.writeFileSync('website/replays/index.html', replaysContents);
fs.writeFileSync('replay.pokemonshowdown.com/index.html', replaysContents);
console.log("DONE");
}

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 318 B

View File

@ -0,0 +1,7 @@
RewriteEngine on
RewriteRule ^api(/.*)?$ http://localhost:9000/api$1 [P,L]
RewriteRule ^([A-Za-z0-9-]+)$ replay.php?name=$1 [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)/manage$ replay-manage.php?name=$1&manage [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)\.log$ replay.log.php?name=$1 [L,QSA]
RewriteRule ^([A-Za-z0-9-]+)\.json$ replay.log.php?json&name=$1 [L,QSA]

View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Replay Not Found - Pok&eacute;mon Showdown!</title>
<link rel="stylesheet" href="//pokemonshowdown.com/style/global.css?0.20436301759273556" />
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/font-awesome.css?932f42c7" />
<!-- 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 -->
<div class="body">
<header>
<div class="nav-wrapper"><ul class="nav">
<li><a class="button nav-first" href="//pokemonshowdown.com/"><img src="//pokemonshowdown.com/images/pokemonshowdownbeta.png" srcset="//pokemonshowdown.com/images/pokemonshowdownbeta.png 1x, //pokemonshowdown.com/images/pokemonshowdownbeta@2x.png 2x" alt="Pok&eacute;mon Showdown" width="146" height="44" /> Home</a></li>
<li><a class="button" href="//pokemonshowdown.com/dex/">Pok&eacute;dex</a></li>
<li><a class="button cur" href="/">Replays</a></li>
<li><a class="button" href="//pokemonshowdown.com/ladder/">Ladder</a></li>
<li><a class="button nav-last" href="//pokemonshowdown.com/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">
<section class="section" style="max-width:200px">
<div style="text-align:center">
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-n.gif" alt="" style="image-rendering: pixelated"
/><img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-o.gif" alt="" style="image-rendering: pixelated"
/><img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-t.gif" alt="" style="image-rendering: pixelated" />
</div>
<div style="text-align:center">
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-f.gif" alt="" style="image-rendering: pixelated"
/><img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-o.gif" alt="" style="image-rendering: pixelated"
/><img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-u.gif" alt="" style="image-rendering: pixelated"
/><img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-n.gif" alt="" style="image-rendering: pixelated"
/><img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-d.gif" alt="" style="image-rendering: pixelated" />
</div>
</section><section class="section">
<h1>Not Found</h1>
<p>
The battle you're looking for has expired. Battles expire after 15 minutes of inactivity unless they're saved.
</p>
<p>
In the future, remember to click <strong>Upload and share replay</strong> to save a replay permanently.
</p>
</section>
</div>
</div>
<script>
if (window.matchMedia) {
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.className = 'dark';
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function (event) {
document.documentElement.className = event.matches ? "dark" : "";
});
}
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -0,0 +1,17 @@
<?php
/**
* Literally only used for the Replays webapp to know which username's
* private replays you're allowed to view.
*
* TODO: Move to loginserver.
*
* @author Guangcong Luo <guangcongluo@gmail.com>
* @license MIT
*/
require '../lib/ntbb-session.lib.php';
echo ']' . ($curuser['loggedin'] ? $curuser['userid'] : '') . ',';
echo $users->isSysop() ? '1' : '';

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -4,7 +4,21 @@
<title>Replays - Pok&eacute;mon Showdown!</title>
<link rel="stylesheet" href="/style/global.css?" />
<!--
Hey, you! Looking in the source for the replay log?
You can find them in JSON format, just add `.json` at the end of a replay URL.
https://replay.pokemonshowdown.com/gen7randomdoublesbattle-865046831.json
Or, if you only need the log itself, add `.log` instead:
https://replay.pokemonshowdown.com/gen7randomdoublesbattle-865046831.log
-->
<link rel="stylesheet" href="//pokemonshowdown.com/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" />
@ -122,11 +136,11 @@
<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/pokemonshowdownbeta@2x.png 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 nav-first" href="//pokemonshowdown.com/"><img src="//pokemonshowdown.com/images/pokemonshowdownbeta.png" srcset="//pokemonshowdown.com/images/pokemonshowdownbeta.png 1x, //pokemonshowdown.com/images/pokemonshowdownbeta@2x.png 2x" alt="Pok&eacute;mon Showdown" width="146" height="44" /> Home</a></li>
<li><a class="button" href="//pokemonshowdown.com/dex/">Pok&eacute;dex</a></li>
<li><a class="button cur" href="/">Replays</a></li>
<li><a class="button" href="//pokemonshowdown.com/ladder/">Ladder</a></li>
<li><a class="button nav-last" href="//pokemonshowdown.com/forums/">Forum</a></li>
<li><a class="button greenbutton nav-first nav-last" href="//play.pokemonshowdown.com/">Play</a></li>
</ul></div>
</header>

View File

@ -1,22 +1,22 @@
<?php
/**
* replay.log.php
*
* Serves `[replay].log` and `[replay].json` APIs. The latter is used by
* replay.pokemonshowdown.com and also the sim's built-in replay viewer;
* the former is purely an external API.
*
* @author Guangcong Luo <guangcongluo@gmail.com>
* @license MIT
*/
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
$manage = false;
// if (@$_REQUEST['name'] === 'smogtours-ou-509') {
// header('Content-type: text/plain');
// readfile('js/smogtours-ou-509.log');
// die();
// }
// if (@$_REQUEST['name'] === 'ou-305002749') {
// header('Content-type: text/plain');
// readfile('js/ou-305002749.log');
// die();
// }
require_once 'replays.lib.php';
$replay = null;
@ -31,8 +31,21 @@ if (substr($id, -2) === 'pw') {
// die($id . ' ' . $password);
}
// $forcecache = isset($_REQUEST['forcecache8723']);
$forcecache = false;
if ($id) {
$replay = $Replays->get($id);
if (file_exists('caches/' . $id . '.inc.php')) {
include 'caches/' . $id . '.inc.php';
$replay['formatid'] = '';
$cached = true;
} else {
require_once 'replays.lib.php';
if (!$Replays->db && !$forcecache) {
header('HTTP/1.1 503 Service Unavailable');
die();
}
$replay = $Replays->get($id, $forcecache);
}
}
if (!$replay) {
header('HTTP/1.1 404 Not Found');

View File

@ -0,0 +1,64 @@
<?php
/**
* replay.php
*
* Checks if a replay exists, used to decide whether to serve the Replays
* webapp or a 404 page. The Replays webapp can also display a 404, but
* doing it here will tell Google and other crawlers which replays
* actually exists.
*
* @author Guangcong Luo <guangcongluo@gmail.com>
* @license MIT
*/
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
$manage = false;
require_once 'replays.lib.php';
$replay = null;
$id = $_REQUEST['name'] ?? '';
$password = '';
$fullid = $id;
if (substr($id, -2) === 'pw') {
$dashpos = strrpos($id, '-');
$password = substr($id, $dashpos + 1, -2);
$id = substr($id, 0, $dashpos);
// die($id . ' ' . $password);
}
// $forcecache = isset($_REQUEST['forcecache8723']);
$forcecache = false;
if ($id) {
if (file_exists('caches/' . $id . '.inc.php')) {
include 'caches/' . $id . '.inc.php';
$replay['formatid'] = '';
$cached = true;
} else {
require_once 'replays.lib.php';
if (!$Replays->db && !$forcecache) {
header('HTTP/1.1 503 Service Unavailable');
die();
}
$replay = $Replays->exists($id, $forcecache);
}
}
if (!$replay) {
header('HTTP/1.1 404 Not Found');
include '404.html';
die();
}
if ($replay['password'] ?? null) {
if ($password !== $replay['password']) {
header('HTTP/1.1 404 Not Found');
include '404.html';
die();
}
}
include 'index.html';

View File

@ -65,6 +65,21 @@ class Replays {
return $replay;
}
function exists($id, $forcecache = false) {
if ($forcecache) return null;
if (!$this->db) {
$this->init();
}
$res = $this->db->prepare("SELECT id, password FROM ps_replays WHERE id = ? LIMIT 1");
$res->execute([$id]);
if (!$res) return null;
$replay = $res->fetch();
if (!$replay) return null;
return $replay;
}
function edit(&$replay) {
if ($replay['private'] === 3) {
$replay['private'] = 3;

View File

@ -1,3 +1,5 @@
// preserved for replay management
/* function updateProgress(done, a, b)
{
if (!battle.paused) return;

View File

@ -3,9 +3,9 @@ import preact from 'preact';
import $ from 'jquery';
import {Net} from './utils';
import {PSRouter} from './replays';
import {Battle} from '../../../src/battle';
import {BattleLog} from '../../../src/battle-log';
import {BattleSound} from '../../../src/battle-sound';
import {Battle} from '../../src/battle';
import {BattleLog} from '../../src/battle-log';
import {BattleSound} from '../../src/battle-sound';
declare function toID(input: string): string;
function showAd(id: string) {
@ -94,7 +94,7 @@ export class BattlePanel extends preact.Component<{id: string}> {
this.battle = null;
this.result = undefined;
Net(`https://replay.pokemonshowdown.com/${this.stripQuery(id)}.json`).get().then(result => {
Net(`/${this.stripQuery(id)}.json`).get().then(result => {
const replay: NonNullable<BattlePanel['result']> = JSON.parse(result);
this.result = replay;
const $base = $(this.base!);

View File

@ -26,7 +26,7 @@ class SearchPanel extends preact.Component<{id: string}> {
loggedInUserIsSysop = false;
sort = 'date';
override componentDidMount() {
Net('check-login.php').get().then(result => {
Net('/check-login.php').get().then(result => {
if (result.charAt(0) !== ']') return;
const [userid, sysop] = result.slice(1).split(',');
this.loggedInUser = userid;
@ -262,7 +262,7 @@ class FeaturedReplays extends preact.Component {
<strong>kdarewolf</strong> vs. <strong>Onox</strong>
<small><br />Protean + prediction</small>
</a></li>
<li><a href="anythinggoes-218380995" class="blocklink">
<li><a href="anythinggoes-218380995?p2" class="blocklink">
<small>[gen6-anythinggoes]<br /></small>
<strong>Anta2</strong> vs. <strong>dscottnew</strong>
<small><br />Cheek Pouch</small>

View File

@ -1,7 +0,0 @@
<?php
require '../../lib/ntbb-session.lib.php';
echo ']' . ($curuser['loggedin'] ? $curuser['userid'] : '') . ',';
echo $users->isSysop() ? '1' : '';

View File

@ -79,7 +79,8 @@ header {
background: linear-gradient(to bottom, #273661, #4c63a3);
box-shadow: 0.5px 1px 2px rgba(255, 255, 255, 0.45), inset 0.5px 1px -1px rgba(255, 255, 255, 0.5);
}
.nav a.cur, .nav a.cur:hover, .nav a.cur:active {
.nav a.cur, .nav a.cur:hover, .nav a.cur:active,
.dark .nav a.cur, .dark .nav a.cur:hover, .dark .nav a.cur:active {
color: #CCCCCC;
background: rgba(79, 109, 148, 0.7);
box-shadow: 0.5px 1px 2px rgba(255, 255, 255, 0.45);