This commit is contained in:
41666 2024-07-15 14:13:23 -04:00
parent a5bffa763e
commit 752041a375
70 changed files with 7484 additions and 7443 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake;

1
.gitignore vendored
View file

@ -8,3 +8,4 @@ node_modules
.DS_Store
**/.DS_Store
.direnv

View file

@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import type { MetagameWorld } from "~/utils/metagame";
import { humanTimeAgo } from "~/utils/strings";
import type { MetagameWorld } from "../utils/metagame";
import { humanTimeAgo } from "../utils/strings";
import * as styles from "./alert-timer.css";
const endTime = (alert: Required<MetagameWorld["zones"][0]>["alert"]) => {
@ -51,7 +51,7 @@ export const AlertTimer = ({
}: {
alert: MetagameWorld["zones"][0]["alert"];
}) => {
const [timeLeft, setTimeLeft] = useState(timeLeftString(alert));
const [timeLeft, setTimeLeft] = useState(<>s</>);
useEffect(() => {
if (alert) {

View file

@ -0,0 +1,15 @@
import { style } from "@vanilla-extract/css";
import { background100, background200 } from "../utils/theme";
export const bar = style({
backgroundColor: background200,
width: "0.3em",
height: "1em",
border: `1px solid ${background100}`,
// margin: 2
});
export const container = style({
display: "flex",
});

View file

@ -0,0 +1,41 @@
import { useMemo } from "react";
import { Population } from "../utils/saerro";
import * as styles from "./faction-bar-sxs.css";
import { background200, ncFaction, trFaction, vsFaction } from "../utils/theme";
export const FactionBarSxS = ({
population: { nc, vs, tr },
}: {
population: Population;
}) => {
const { vsPercent, ncPercent, trPercent } = useMemo(() => {
const total = nc + vs + tr;
return {
vsPercent: Math.round((vs / total) * 100) || 0,
ncPercent: Math.round((nc / total) * 100) || 0,
trPercent: Math.round((tr / total) * 100) || 0,
};
}, [vs, nc, tr]);
return (
<div className={styles.container}>
<Bar percent={vsPercent} color={vsFaction} />
<Bar percent={ncPercent} color={ncFaction} />
<Bar percent={trPercent} color={trFaction} />
</div>
);
};
const Bar = (props: { percent: number; color: string }) => (
<div
className={styles.bar}
style={{
backgroundImage: `linear-gradient( to top,
${props.color} 0% ${props.percent}% ,
${background200} ${props.percent + 0.1}% 100%
)`,
}}
>
&nbsp;
</div>
);

View file

@ -1,5 +1,6 @@
import type { ComplexStyleRule } from "@vanilla-extract/css";
import { style } from "@vanilla-extract/css";
import { edge, ncFaction, trFaction, vsFaction } from "../utils/theme";
export const bar = style({
display: "flex",
@ -8,7 +9,7 @@ export const bar = style({
flexDirection: "row",
overflow: "hidden",
borderRadius: "0.4rem",
border: "2px solid #4d4d4d",
border: `2px solid ${edge}`,
});
export const tinyBar = style({
@ -27,16 +28,16 @@ const shared: ComplexStyleRule = {
export const left = style({
...shared,
backgroundColor: "#991cba",
backgroundColor: vsFaction,
});
export const center = style({
...shared,
backgroundColor: "#1564cc",
borderLeft: "1px solid #4d4d4d",
borderRight: "2px solid #4d4d4d",
backgroundColor: ncFaction,
borderLeft: `1px solid ${edge}`,
borderRight: `2px solid ${edge}`,
boxShadow: "inset 0 0 0.5rem rgb(180 180 180 / 10%)",
});
export const right = style({
...shared,
backgroundColor: "#d30101",
backgroundColor: trFaction,
});

View file

@ -1,5 +1,5 @@
import { useMemo } from "react";
import type { Population } from "~/utils/saerro";
import type { Population } from "../utils/saerro";
import * as styles from "./faction-bar.css";
export const FactionBar = ({

View file

@ -1,4 +1,5 @@
import type { Population } from "~/utils/saerro";
import { background200, ncFaction, trFaction, vsFaction } from "../utils/theme";
import type { Population } from "../utils/saerro";
import { pieRoot } from "./faction-pie.css";
export const FactionPie = ({
@ -24,10 +25,11 @@ export const FactionPie = ({
style={
{
fontSize: size || "1em",
backgroundColor: background200,
backgroundImage: `conic-gradient(
#d30101 0% ${trPct}%,
#991cba ${trPct}% ${trPct + vsPct}%,
#1564cc ${trPct + vsPct}% 100%
${trFaction} 0% ${trPct}%,
${vsFaction} ${trPct}% ${trPct + vsPct}%,
${ncFaction} ${trPct + vsPct}% 100%
)`,
"--inner-margin": innerMargin ? `${innerMargin}px` : "0",
"--inner-bg": innerBackground || "none",

View file

@ -1,5 +1,5 @@
import { style } from "@vanilla-extract/css";
import footer from "~/images/footer.jpg";
import footer from "../images/footer.jpg";
export const root = style({
height: 300,

View file

@ -1,7 +1,7 @@
import { IndexWorld } from "./index-world";
import * as styles from "./index-world-container.css";
import type { MetagameWorld } from "~/utils/metagame";
import type { PopulationWorld } from "~/utils/population";
import type { MetagameWorld } from "../utils/metagame";
import type { PopulationWorld } from "../utils/population";
export const WorldContainer = ({
metagame,

View file

@ -1,19 +1,13 @@
import { Link } from "@remix-run/react";
import {
humanTimeAgo,
snakeCaseToTitleCase,
worlds,
zones,
} from "~/utils/strings";
import { snakeCaseToTitleCase, worlds, zones } from "../utils/strings";
import * as styles from "./index-world.css";
import vsLogo from "~/images/vs-100.png";
import ncLogo from "~/images/nc-100.png";
import trLogo from "~/images/tr-100.png";
import vsLogo from "../images/vs-100.png";
import ncLogo from "../images/nc-100.png";
import trLogo from "../images/tr-100.png";
import { FactionBar } from "./faction-bar";
import type { MetagameWorld } from "~/utils/metagame";
import type { PopulationWorld } from "~/utils/population";
import { c } from "~/utils/classes";
import { useEffect, useState } from "react";
import type { MetagameWorld } from "../utils/metagame";
import type { PopulationWorld } from "../utils/population";
import { c } from "../utils/classes";
import { AlertTimer } from "./alert-timer";
export type IndexWorldProps = {

View file

@ -0,0 +1,58 @@
import { style } from "@vanilla-extract/css";
export const zoneContainer = style({
margin: "0.5em 1em",
backgroundColor: "#222",
padding: "1em",
boxSizing: "border-box",
});
export const zoneHeader = style({
display: "flex",
});
export const chartTileTotal = style({
textAlign: "center",
lineHeight: 1,
minWidth: "2em",
maxWidth: "2em",
fontSize: "3rem",
overflowY: "hidden",
});
export const chartTilePopLine = style({
display: "flex",
fontSize: "1.5rem",
fontWeight: "bold",
alignItems: "center",
justifyContent: "center",
lineHeight: 1.1,
margin: 0,
});
export const chartTilePopImage = style({
width: "1em",
// height: "1em",
marginRight: 4,
});
export const chartTile = style({
fontSize: "4rem",
flex: "0 3 33%",
display: "flex",
marginTop: "0.2em",
});
export const classesContainer = style({
display: "flex",
flex: "1 3 100%",
flexWrap: "wrap",
justifyContent: "space-evenly",
});
export const chartTileChart = style({
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
});

View file

@ -0,0 +1,98 @@
import {
allClasses,
Population,
totalPopulation,
World,
Zone,
} from "../utils/saerro";
import { headerFont } from "./world.css";
import {
chartTile,
chartTileChart,
chartTilePopImage,
chartTilePopLine,
chartTileTotal,
classesContainer,
zoneContainer,
zoneHeader,
} from "./world-zone-container.css";
import { classIconMap } from "../utils/class-icons";
import { FactionBarSxS } from "./faction-bar-sxs";
import { c } from "../utils/classes";
import vsLogo from "../images/vs-100.png";
import ncLogo from "../images/nc-100.png";
import trLogo from "../images/tr-100.png";
export type WZCProps = {
world: World;
zone: Zone;
};
export const WorldZoneContainer = (props: WZCProps) => {
return (
<section className={zoneContainer} title={props.zone.name}>
<div className={zoneHeader}>
<h3 className={headerFont}>{props.zone.name.toUpperCase()}</h3>
{/* TODO: metagame */}
</div>
<div>
<h4 className={headerFont}>CLASSES</h4>
<div style={{ display: "flex" }}>
<Classes classes={props.zone.classes} />
<div style={{ display: "flex", flex: "1 3 100%", flexWrap: "wrap" }}>
&nbsp;
</div>
<div style={{ display: "flex", flex: "1 3 100%", flexWrap: "wrap" }}>
&nbsp;
</div>
</div>
</div>
</section>
);
};
const Classes = (props: { classes: Zone["classes"] }) => {
if (props.classes === undefined) {
return null;
}
return (
<div className={classesContainer}>
{allClasses.map((name) => (
<ChartTile
key={name}
name={name}
pop={(props.classes as Record<string, Population>)[name]}
/>
))}
</div>
);
};
const ChartTile = (props: { name: string; pop: Population }) => (
<div className={chartTile}>
<div className={chartTileChart}>
<FactionBarSxS population={props.pop} />
<img src={(classIconMap as any)[props.name]} />
</div>
<div>
<div className={c(headerFont, chartTileTotal)}>
{totalPopulation(props.pop)}
</div>
<div>
<div className={chartTilePopLine}>
<img className={chartTilePopImage} src={vsLogo} alt="VS" />{" "}
{props.pop.vs}
</div>
<div className={chartTilePopLine}>
<img className={chartTilePopImage} src={ncLogo} alt="NC" />{" "}
{props.pop.nc}
</div>
<div className={chartTilePopLine}>
<img className={chartTilePopImage} src={trLogo} alt="TR" />{" "}
{props.pop.tr}
</div>
</div>
</div>
</div>
);

View file

@ -28,7 +28,6 @@ export const headerSub = style({
export const outer = style({
display: "flex",
justifyContent: "center",
minHeight: "100vh",
flexDirection: "column",
maxWidth: "1920px",

View file

@ -1,18 +0,0 @@
/**
* By default, Remix will handle hydrating your app on the client for you.
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal`
* For more information, see https://remix.run/file-conventions/entry.client
*/
import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<RemixBrowser />
</StrictMode>
);
});

View file

@ -1,38 +0,0 @@
/**
* By default, Remix will handle generating the HTTP Response for you.
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal`
* For more information, see https://remix.run/file-conventions/entry.server
*/
import type { EntryContext } from "@remix-run/cloudflare";
import { RemixServer } from "@remix-run/react";
import isbot from "isbot";
import { renderToReadableStream } from "react-dom/server";
export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
const body = await renderToReadableStream(
<RemixServer context={remixContext} url={request.url} />,
{
signal: request.signal,
onError(error: unknown) {
console.error(error);
responseStatusCode = 500;
},
}
);
if (isbot(request.headers.get("user-agent"))) {
await body.allReady;
}
responseHeaders.set("Content-Type", "text/html");
return new Response(body, {
headers: responseHeaders,
status: responseStatusCode,
});
}

BIN
app/images/icon_engi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

BIN
app/images/icon_heavy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

BIN
app/images/icon_infil.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,008 B

BIN
app/images/icon_light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
app/images/icon_max.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 B

BIN
app/images/icon_medic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

BIN
app/images/vehicles/ant.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

View file

@ -1,4 +1,4 @@
import type { LinksFunction } from "@remix-run/cloudflare";
import type { LinksFunction } from "@remix-run/node";
import { cssBundleHref } from "@remix-run/css-bundle";
import {
Links,
@ -14,17 +14,11 @@ import "./reset.css";
export const links: LinksFunction = () => [
{
rel: "preconnect",
href: "https://fonts.gstatic.com",
crossOrigin: "anonymous",
},
{
rel: "preconnect",
href: "ttps://fonts.googleapis.com",
crossOrigin: "anonymous",
href: "https://fonts.bunny.net",
},
{
rel: "stylesheet",
href: "https://fonts.googleapis.com/css2?family=Unbounded:wght@700&display=swap",
href: "https://fonts.bunny.net/css?family=unbounded:700",
},
...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []),
@ -43,7 +37,6 @@ export default function App() {
<Outlet />
<ScrollRestoration />
<Scripts />
<LiveReload />
</body>
</html>
);

View file

@ -1,10 +1,10 @@
import { json, type V2_MetaFunction } from "@remix-run/cloudflare";
import { json, type MetaFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { Footer } from "~/components/footer";
import { WorldContainer } from "~/components/index-world-container";
import { outer } from "~/components/index.css";
import { fetchMetagameWorlds } from "~/utils/metagame";
import { fetchPopulationWorlds } from "~/utils/population";
import { Footer } from "../components/footer";
import { WorldContainer } from "../components/index-world-container";
import { outer } from "../components/index.css";
import { fetchMetagameWorlds } from "../utils/metagame";
import { fetchPopulationWorlds } from "../utils/population";
export const loader = async () => {
const [metagame, population] = await Promise.all([
@ -15,7 +15,7 @@ export const loader = async () => {
return json({ metagame: metagame.sort((a, b) => a.id - b.id), population });
};
export const meta: V2_MetaFunction = () => {
export const meta: MetaFunction = () => {
return [
{ title: "PS2.LIVE" },
{

View file

@ -1,4 +1,4 @@
import { Footer } from "~/components/footer";
import { Footer } from "../components/footer";
import {
header,
item,
@ -8,7 +8,7 @@ import {
link,
love,
outer,
} from "~/components/about.css";
} from "../components/about.css";
export default function About() {
return (

View file

@ -1,7 +1,8 @@
import { useState } from "react";
import { FactionBar } from "~/components/faction-bar";
import { FactionPie } from "~/components/faction-pie";
import type { Population } from "~/utils/saerro";
import { FactionBar } from "../components/faction-bar";
import { FactionPie } from "../components/faction-pie";
import type { Population } from "../utils/saerro";
import { FactionBarSxS } from "../components/faction-bar-sxs";
export default function DebugComponents() {
const [population, setPopulation] = useState<Population>({
@ -66,6 +67,10 @@ export default function DebugComponents() {
value={innerColor}
onChange={(e) => setInnerColor(e.target.value)}
/>
<h3>Vertical Side-by-Side Bar Chart</h3>
<div style={{ fontSize: "5rem" }}>
<FactionBarSxS population={population}></FactionBarSxS>
</div>
</div>
</div>
);

View file

@ -1,32 +1,33 @@
import type { LoaderArgs, V2_MetaFunction } from "@remix-run/cloudflare";
import { json } from "@remix-run/cloudflare";
import type { MetaFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { Footer } from "~/components/footer";
import type { MetagameWorld } from "~/utils/metagame";
import { fetchSingleMetagameWorld } from "~/utils/metagame";
import type { WorldResponse, Zone } from "~/utils/saerro";
import { Footer } from "../components/footer";
import type { MetagameWorld } from "../utils/metagame";
import { fetchSingleMetagameWorld } from "../utils/metagame";
import type { WorldResponse, Zone } from "../utils/saerro";
import {
allClasses,
allVehicles,
totalPopulation,
worldQuery,
} from "~/utils/saerro";
} from "../utils/saerro";
import {
pascalCaseToTitleCase,
toTitleCase,
worlds,
zones,
} from "~/utils/strings";
import * as styles from "~/components/world.css";
import { c } from "~/utils/classes";
import { FactionBar } from "~/components/faction-bar";
import { popImage } from "~/components/index-world.css";
import vsLogo from "~/images/vs-100.png";
import ncLogo from "~/images/nc-100.png";
import trLogo from "~/images/tr-100.png";
import { FactionPie } from "~/components/faction-pie";
import { AlertTimer } from "~/components/alert-timer";
import { contPrioritySort } from "~/utils/sorting";
} from "../utils/strings";
import * as styles from "../components/world.css";
import { c } from "../utils/classes";
import { FactionBar } from "../components/faction-bar";
import { popImage } from "../components/index-world.css";
import vsLogo from "../images/vs-100.png";
import ncLogo from "../images/nc-100.png";
import trLogo from "../images/tr-100.png";
import { FactionPie } from "../components/faction-pie";
import { AlertTimer } from "../components/alert-timer";
import { contPrioritySort, zonePopulationSort } from "../utils/sorting";
import { WorldZoneContainer } from "../components/world-zone-container";
type LoaderData = {
saerro: WorldResponse;
@ -34,7 +35,7 @@ type LoaderData = {
id: string;
};
export async function loader({ params }: LoaderArgs) {
export async function loader({ params }) {
const [saerro, metagame] = await Promise.all([
worldQuery(params.id as string),
fetchSingleMetagameWorld(params.id as string),
@ -42,7 +43,7 @@ export async function loader({ params }: LoaderArgs) {
return json({ saerro, metagame, id: params.id } as LoaderData);
}
export const meta: V2_MetaFunction<typeof loader> = ({ data }) => {
export const meta: MetaFunction<typeof loader> = ({ data }) => {
const { saerro, id } = data as LoaderData;
const date = new Date();
const worldInfo = worlds[String(id || "default")];
@ -168,10 +169,13 @@ export default function World() {
</div>
</div>
<div>
<h2>Continents</h2>
{world.zones.all.map((zone) => (
<ZoneInfo zone={zone} key={zone.id} />
))}
<h2 className={styles.headerFont}>WARZONES</h2>
<div>
{world.zones.all.sort(zonePopulationSort).map((zone) => (
<WorldZoneContainer key={zone.id} world={world} zone={zone} />
// <ZoneInfo key={zone.id} zone={zone} />
))}
</div>
</div>
</div>
</div>
@ -183,8 +187,8 @@ export default function World() {
const ZoneInfo = ({ zone }: { zone: Zone }) => {
const zoneInfo = zones[String(zone.id)];
return (
<section>
<h3>{zoneInfo.name}</h3>
<section className={styles.zone}>
<h3 className={styles.headerFont}>{zoneInfo.name.toUpperCase()}</h3>
<p>
{totalPopulation(zone.population)} players ({zone.population.vs} VS,{" "}
{zone.population.nc} NC, {zone.population.tr} TR)

17
app/utils/class-icons.ts Normal file
View file

@ -0,0 +1,17 @@
import combatMedic from "../images/icon_medic.png";
import engineer from "../images/icon_engi.png";
import heavyAssault from "../images/icon_heavy.png";
import infiltrator from "../images/icon_infil.png";
import lightAssault from "../images/icon_light.png";
import max from "../images/icon_max.png";
export { combatMedic, engineer, heavyAssault, infiltrator, lightAssault, max };
export const classIconMap = {
combatMedic,
engineer,
heavyAssault,
infiltrator,
lightAssault,
max,
};

View file

@ -1,11 +1,6 @@
export const saerroFetch = async <T>(query: string): Promise<T> => {
const response = await fetch(
`https://saerro.ps2.live/graphql?query=${query}`,
{
cf: {
cacheTtl: 60,
},
}
`https://saerro.ps2.live/graphql?query=${query}`
);
const json: { data: T } = await response.json();
return json.data;
@ -22,10 +17,10 @@ export type Zone = {
id: string;
name: string;
population: Population;
vehicles?: Record<typeof allVehicles[number], Population> & {
vehicles?: Record<(typeof allVehicles)[number], Population> & {
total: number;
};
classes?: Record<typeof allClasses[number], Population>;
classes?: Record<(typeof allClasses)[number], Population>;
};
export type World = {
@ -83,6 +78,7 @@ export const worldQuery = async (worldID: string): Promise<WorldResponse> => {
zones {
all {
id
name
classes {
${allClasses.map((cls) => `${cls} { total nc tr vs }`).join(" ")}
}

View file

@ -1,4 +1,5 @@
import type { MetagameWorld } from "./metagame";
import { totalPopulation, type Zone } from "./saerro";
export const contPrioritySort = (
a: MetagameWorld["zones"][number],
@ -41,3 +42,24 @@ export const contPrioritySort = (
return 0;
};
export const zonePopulationSort = (a: Zone, b: Zone): number => {
const total = ({ nc, vs, tr }: { nc: number; vs: number; tr: number }) =>
nc + vs + tr;
const ap = total(a.population);
const bp = total(b.population);
if (ap < bp) {
return 1;
}
if (ap > bp) {
return -1;
}
if (ap === bp) {
return a.id < b.id ? 1 : -1;
}
return 0;
};

8
app/utils/theme.ts Normal file
View file

@ -0,0 +1,8 @@
export const trFaction = "#d30101";
export const ncFaction = "#1564cc";
export const vsFaction = "#991cba";
export const background100 = "#222";
export const background200 = "#444";
export const edge = "#4d4d4d";

View file

@ -0,0 +1,60 @@
import ant from "../images/vehicles/ant.png";
import chimera from "../images/vehicles/chimera.png";
import corsair from "../images/vehicles/corsair.png";
import dervish from "../images/vehicles/dervish.png";
import flash from "../images/vehicles/flash.png";
import galaxy from "../images/vehicles/galaxy.png";
import harasser from "../images/vehicles/harasser.png";
import javelin from "../images/vehicles/javelin.png";
import liberator from "../images/vehicles/liberator.png";
import lightning from "../images/vehicles/lightning.png";
import magrider from "../images/vehicles/magrider.png";
import mosquito from "../images/vehicles/mosquito.png";
import prowler from "../images/vehicles/prowler.png";
import reaver from "../images/vehicles/reaver.png";
import scythe from "../images/vehicles/scythe.png";
import sunderer from "../images/vehicles/sunderer.png";
import valkyrie from "../images/vehicles/valkyrie.png";
import vanguard from "../images/vehicles/vanguard.png";
export {
ant,
chimera,
corsair,
dervish,
flash,
galaxy,
harasser,
javelin,
liberator,
lightning,
magrider,
mosquito,
prowler,
reaver,
scythe,
sunderer,
valkyrie,
vanguard,
};
export const vehicleIconMap = {
ant,
chimera,
corsair,
dervish,
flash,
galaxy,
harasser,
javelin,
liberator,
lightning,
magrider,
mosquito,
prowler,
reaver,
scythe,
sunderer,
valkyrie,
vanguard,
};

1904
build/index.js Normal file

File diff suppressed because it is too large Load diff

7
build/index.js.map Normal file

File diff suppressed because one or more lines are too long

1
build/metafile.css.json Normal file

File diff suppressed because one or more lines are too long

1
build/metafile.js.json Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
build/version.txt Normal file
View file

@ -0,0 +1 @@
918ff520

BIN
bun.lockb Executable file

Binary file not shown.

58
flake.lock generated Normal file
View file

@ -0,0 +1,58 @@
{
"nodes": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1717285511,
"narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1718318537,
"narHash": "sha256-4Zu0RYRcAY/VWuu6awwq4opuiD//ahpc2aFHg2CWqFY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e9ee548d90ff586a6471b4ae80ae9cfcbceb3420",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1717284937,
"narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

23
flake.nix Normal file
View file

@ -0,0 +1,23 @@
{
description = "https://noe.sh";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
};
outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ "x86_64-linux" "aarch64-linux" ];
perSystem = { config, self', pkgs, lib, system, ... }: {
devShells.default = import ./shell.nix { inherit pkgs; };
packages.default = pkgs.stdenvNoCC.mkDerivation {
name = "noe.sh";
src = ./.;
installPhase = ''
cp -r . $out/
'';
};
};
};
}

View file

@ -0,0 +1,42 @@
import { writeFileSync } from "fs";
import { join } from "path";
// thanks varoombaa
const vehicles = [
["sunderer", 2, 264],
["flash", 1, 262],
["lightning", 3, 258],
["magrider", 4, 259],
["vanguard", 5, 265],
["prowler", 6, 261],
["scythe", 7, 266],
["reaver", 8, 263],
["mosquito", 9, 260],
["liberator", 10, 257],
["galaxy", 11, 256],
["harasser", 12, 8852],
["valkyrie", 14, 79711],
["ant", 15, 84726],
["glaive", 163, 264],
["flash", 1001, 260],
["liberator", 1010, 258],
["galaxy", 1011, 256],
["colossus", 2007, 92801],
["bastion", 2019, 92392],
["javelin", 2033, 92332],
["dervish", 2136, 93607],
["chimera", 2137, 93604],
["corsair", 2142, 95012],
];
for (let [name, , imageID] of vehicles) {
const response = await fetch(
`https://census.daybreakgames.com/files/ps2/images/static/${imageID}.png`
);
const buf = await response.arrayBuffer();
const outPath = `./app/images/vehicles/${name}.png`;
writeFileSync(outPath, Buffer.from(buf));
console.log(`wrote ${name} (${imageID}) to ${outPath}`);
}

12214
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,39 +2,39 @@
"name": "ps2.live",
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"build": "remix build",
"dev:remix": "remix watch",
"dev:wrangler": "npm run wrangler",
"dev": "cross-env NODE_ENV=development npm-run-all build --parallel \"dev:*\"",
"start": "cross-env NODE_ENV=production npm run wrangler --live-reload",
"typecheck": "tsc",
"wrangler": "wrangler pages dev ./public",
"pages:deploy": "npm run build && wrangler pages publish ./public"
"build": "remix vite:build",
"dev": "node ./server.mjs",
"typecheck": "tsc"
},
"dependencies": {
"@remix-run/cloudflare": "^1.17.0",
"@remix-run/cloudflare-pages": "^1.17.0",
"@remix-run/css-bundle": "^1.17.0",
"@remix-run/react": "^1.17.0",
"@remix-run/css-bundle": "^2.9.2",
"@remix-run/express": "^2.9.2",
"@remix-run/node": "^2.9.2",
"@remix-run/react": "^2.9.2",
"cross-env": "^7.0.3",
"isbot": "^3.6.10",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"express": "^4.19.2",
"isbot": "^4.4.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@cloudflare/workers-types": "3.x",
"@remix-run/dev": "^1.17.0",
"@remix-run/eslint-config": "^1.17.0",
"@types/react": "^18.2.10",
"@types/react-dom": "^18.2.4",
"@vanilla-extract/css": "^1.11.1",
"@remix-run/dev": "^2.9.2",
"@remix-run/eslint-config": "^2.9.2",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vanilla-extract/css": "^1.15.3",
"@vanilla-extract/vite-plugin": "^4.0.11",
"eslint": "^8.42.0",
"npm-run-all": "^4.1.5",
"typescript": "^5.1.3",
"wrangler": "^3.1.0"
"typescript": "^5.4.5",
"vite": "^5.3.1"
},
"engines": {
"node": ">=16.13"
}
"description": "- [Remix Docs](https://remix.run/docs)",
"version": "1.0.0",
"main": ".eslintrc.js",
"keywords": [],
"author": "",
"license": "ISC"
}

View file

@ -1,23 +0,0 @@
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
devServerBroadcastDelay: 1000,
ignoredRouteFiles: ["**/.*"],
server: "./server.ts",
serverBuildPath: "functions/[[path]].js",
serverConditions: ["worker"],
serverDependenciesToBundle: "all",
serverMainFields: ["browser", "module", "main"],
serverMinify: true,
serverModuleFormat: "esm",
serverPlatform: "neutral",
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
// publicPath: "/build/",
future: {
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,
v2_normalizeFormMethod: true,
v2_routeConvention: true,
},
};

2
remix.env.d.ts vendored
View file

@ -1,3 +1 @@
/// <reference types="@remix-run/dev" />
/// <reference types="@remix-run/cloudflare" />
/// <reference types="@cloudflare/workers-types" />

26
server.mjs Normal file
View file

@ -0,0 +1,26 @@
import { createRequestHandler } from "@remix-run/express";
import express from "express";
const viteDevServer =
process.env.NODE_ENV === "production"
? null
: await import("vite").then((vite) =>
vite.createServer({
server: { middlewareMode: true },
})
);
const app = express();
app.use(
viteDevServer ? viteDevServer.middlewares : express.static("build/client")
);
const build = viteDevServer
? () => viteDevServer.ssrLoadModule("virtual:remix/server-build")
: await import("./build/server/index.js");
app.all("*", createRequestHandler({ build }));
app.listen(3000, () => {
console.log("App listening on http://localhost:3000");
});

View file

@ -1,8 +0,0 @@
import { createPagesFunctionHandler } from "@remix-run/cloudflare-pages";
import * as build from "@remix-run/dev/server-build";
export const onRequest = createPagesFunctionHandler({
build,
getLoadContext: (context) => context.env,
mode: process.env.NODE_ENV,
});

8
shell.nix Normal file
View file

@ -0,0 +1,8 @@
{ pkgs ? import <nixpkgs> {} }: pkgs.mkShell {
buildInputs = with pkgs; [
nodePackages.serve
nodePackages.prettier
nodejs
bun
];
}

View file

@ -12,9 +12,6 @@
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"~/*": ["./app/*"]
},
// Remix takes care of building everything in `remix build`.
"noEmit": true

7
vite.config.js Normal file
View file

@ -0,0 +1,7 @@
import { vitePlugin as remix } from "@remix-run/dev";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [remix(), vanillaExtractPlugin()],
});

View file

@ -1 +0,0 @@
compatibility_date = "2023-06-10"