refacto: Displaying number of players for each team + adding logo at registration
This commit is contained in:
@@ -48,11 +48,19 @@
|
|||||||
<div class="authTeamChoice">
|
<div class="authTeamChoice">
|
||||||
<label class="authTeamOption">
|
<label class="authTeamOption">
|
||||||
<input type="radio" name="regTeam" value="blue" required />
|
<input type="radio" name="regTeam" value="blue" required />
|
||||||
<span class="authTeamBadge authTeamBadge--blue">Résistance</span>
|
<span class="authTeamBadge authTeamBadge--blue">
|
||||||
|
<img src="./graphism/logo_resistance.svg" class="authTeamLogo" alt="" />
|
||||||
|
Résistance
|
||||||
|
</span>
|
||||||
|
<span class="authTeamCount authTeamCount--blue" id="regCountBlue">… joueurs</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="authTeamOption">
|
<label class="authTeamOption">
|
||||||
<input type="radio" name="regTeam" value="red" />
|
<input type="radio" name="regTeam" value="red" />
|
||||||
<span class="authTeamBadge authTeamBadge--red">Premier ordre</span>
|
<span class="authTeamBadge authTeamBadge--red">
|
||||||
|
Premier ordre
|
||||||
|
<img src="./graphism/logo_first_order.svg" class="authTeamLogo" alt="" />
|
||||||
|
</span>
|
||||||
|
<span class="authTeamCount authTeamCount--red" id="regCountRed">… joueurs</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -78,7 +86,10 @@
|
|||||||
|
|
||||||
<!-- Team score display -->
|
<!-- Team score display -->
|
||||||
<div class="scoreBoard" id="scoreBoard">
|
<div class="scoreBoard" id="scoreBoard">
|
||||||
|
<div class="teamLogoWrap">
|
||||||
<img src="./graphism/logo_resistance.svg" alt="Resistance" class="team-logo" />
|
<img src="./graphism/logo_resistance.svg" alt="Resistance" class="team-logo" />
|
||||||
|
<span class="teamPlayerCount teamPlayerCount--blue" id="activeCountBlue">0 joueur</span>
|
||||||
|
</div>
|
||||||
<div class="scoreBoardContent">
|
<div class="scoreBoardContent">
|
||||||
<div class="scoreBoardRow">
|
<div class="scoreBoardRow">
|
||||||
<div class="scoreTeam scoreTeam--blue">
|
<div class="scoreTeam scoreTeam--blue">
|
||||||
@@ -133,7 +144,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="teamLogoWrap">
|
||||||
<img src="./graphism/logo_first_order.svg" alt="First Order" class="team-logo" />
|
<img src="./graphism/logo_first_order.svg" alt="First Order" class="team-logo" />
|
||||||
|
<span class="teamPlayerCount teamPlayerCount--red" id="activeCountRed">0 joueur</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Info rows -->
|
<!-- Info rows -->
|
||||||
|
|||||||
@@ -99,3 +99,15 @@ export async function apiFetchVictoryPoints() {
|
|||||||
if (!res.ok) throw new Error("vp_fetch_failed");
|
if (!res.ok) throw new Error("vp_fetch_failed");
|
||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function apiFetchPlayerCounts() {
|
||||||
|
const res = await fetch("/api/auth/player-counts");
|
||||||
|
if (!res.ok) throw new Error("player_counts_fetch_failed");
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function apiFetchActivePlayers() {
|
||||||
|
const res = await fetch("/api/active-players");
|
||||||
|
if (!res.ok) throw new Error("active_players_fetch_failed");
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { apiLogin, apiRegister, apiGetMe } from "./api.js";
|
import { apiLogin, apiRegister, apiGetMe, apiFetchPlayerCounts } from "./api.js";
|
||||||
import { setCurrentTeam, refreshFromServer } from "./game.js";
|
import { setCurrentTeam, refreshFromServer } from "./game.js";
|
||||||
|
|
||||||
// ── DOM refs ──────────────────────────────────────────────────────────────────
|
// ── DOM refs ──────────────────────────────────────────────────────────────────
|
||||||
@@ -15,6 +15,8 @@ const regUsernameEl = document.getElementById("regUsername");
|
|||||||
const regEmailEl = document.getElementById("regEmail");
|
const regEmailEl = document.getElementById("regEmail");
|
||||||
const regPasswordEl = document.getElementById("regPassword");
|
const regPasswordEl = document.getElementById("regPassword");
|
||||||
const registerErrorEl = document.getElementById("registerError");
|
const registerErrorEl = document.getElementById("registerError");
|
||||||
|
const regCountBlueEl = document.getElementById("regCountBlue");
|
||||||
|
const regCountRedEl = document.getElementById("regCountRed");
|
||||||
const userDisplayEl = document.getElementById("userDisplay");
|
const userDisplayEl = document.getElementById("userDisplay");
|
||||||
const logoutBtn = document.getElementById("logoutBtn");
|
const logoutBtn = document.getElementById("logoutBtn");
|
||||||
|
|
||||||
@@ -68,6 +70,15 @@ export async function tryRestoreSession() {
|
|||||||
|
|
||||||
// ── Tab switching ─────────────────────────────────────────────────────────────
|
// ── Tab switching ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
async function loadRegisterCounts() {
|
||||||
|
try {
|
||||||
|
const counts = await apiFetchPlayerCounts();
|
||||||
|
const fmt = (n) => `${n} joueur${n > 1 ? "s" : ""}`;
|
||||||
|
regCountBlueEl.textContent = fmt(counts.blue ?? 0);
|
||||||
|
regCountRedEl.textContent = fmt(counts.red ?? 0);
|
||||||
|
} catch { /* silently ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
tabLogin.addEventListener("click", () => {
|
tabLogin.addEventListener("click", () => {
|
||||||
tabLogin.classList.add("authTab--active");
|
tabLogin.classList.add("authTab--active");
|
||||||
tabRegister.classList.remove("authTab--active");
|
tabRegister.classList.remove("authTab--active");
|
||||||
@@ -82,6 +93,7 @@ tabRegister.addEventListener("click", () => {
|
|||||||
registerForm.classList.remove("hidden");
|
registerForm.classList.remove("hidden");
|
||||||
loginForm.classList.add("hidden");
|
loginForm.classList.add("hidden");
|
||||||
clearError(registerErrorEl);
|
clearError(registerErrorEl);
|
||||||
|
loadRegisterCounts();
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Login form ────────────────────────────────────────────────────────────────
|
// ── Login form ────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { fnv1a32, hash2u32, mulberry32 } from "./rng.js";
|
import { fnv1a32, hash2u32, mulberry32 } from "./rng.js";
|
||||||
import { formatPlanet, generatePlanet } from "./planetGeneration.js";
|
import { formatPlanet, generatePlanet } from "./planetGeneration.js";
|
||||||
import { apiFetchConfig, apiFetchScores, apiFetchGrid, apiRevealCell, apiFetchEconScores, apiTickEconScores, apiFetchElementBonus, apiTickElementBonus, apiFetchDbInfo, apiFetchVictoryPoints } from "./api.js";
|
import { apiFetchConfig, apiFetchScores, apiFetchGrid, apiRevealCell, apiFetchEconScores, apiTickEconScores, apiFetchElementBonus, apiTickElementBonus, apiFetchDbInfo, apiFetchVictoryPoints, apiFetchActivePlayers } from "./api.js";
|
||||||
import { computeTeamIncome, computeTeamElementBonus, computeTeamElementBonusDetailed, renderResourceTable, renderElementBonusTable, setEconSort, getEconSort, setElemSort, getElemSort } from "./economy.js";
|
import { computeTeamIncome, computeTeamElementBonus, computeTeamElementBonusDetailed, renderResourceTable, renderElementBonusTable, setEconSort, getEconSort, setElemSort, getElemSort } from "./economy.js";
|
||||||
|
|
||||||
// ── Constants ─────────────────────────────────────────────────────────────────
|
// ── Constants ─────────────────────────────────────────────────────────────────
|
||||||
@@ -164,6 +164,8 @@ const econScoreRedEl = document.getElementById("econScoreRed");
|
|||||||
const econDeltaBlueEl = document.getElementById("econDeltaBlue");
|
const econDeltaBlueEl = document.getElementById("econDeltaBlue");
|
||||||
const econDeltaRedEl = document.getElementById("econDeltaRed");
|
const econDeltaRedEl = document.getElementById("econDeltaRed");
|
||||||
const elemBonusTableEl = document.getElementById("elementBonusTableBody");
|
const elemBonusTableEl = document.getElementById("elementBonusTableBody");
|
||||||
|
const activeCountBlueEl = document.getElementById("activeCountBlue");
|
||||||
|
const activeCountRedEl = document.getElementById("activeCountRed");
|
||||||
// ── Cell helpers ──────────────────────────────────────────────────────────────
|
// ── Cell helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export function cellKey(x, y) { return `${x},${y}`; }
|
export function cellKey(x, y) { return `${x},${y}`; }
|
||||||
@@ -243,6 +245,15 @@ export async function loadVictoryPoints() {
|
|||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchAndApplyActivePlayers() {
|
||||||
|
try {
|
||||||
|
const { blue, red } = await apiFetchActivePlayers();
|
||||||
|
const fmt = (n) => `${n} joueur${n > 1 ? "s" : ""}`;
|
||||||
|
if (activeCountBlueEl) activeCountBlueEl.textContent = fmt(blue ?? 0);
|
||||||
|
if (activeCountRedEl) activeCountRedEl.textContent = fmt(red ?? 0);
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
// ── Element bonus ─────────────────────────────────────────────────────────────
|
// ── Element bonus ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
let elemBonusBlue = 0;
|
let elemBonusBlue = 0;
|
||||||
@@ -576,16 +587,16 @@ function applyRevealPayload(cell) {
|
|||||||
details.classList.remove("details--hidden");
|
details.classList.remove("details--hidden");
|
||||||
if (!cell.exploitable) {
|
if (!cell.exploitable) {
|
||||||
hint.textContent = `(${cell.x},${cell.y}) Inexploitable`;
|
hint.textContent = `(${cell.x},${cell.y}) Inexploitable`;
|
||||||
details.textContent = `Cell (${cell.x},${cell.y})\n\nStatus : Inexploitable`;
|
details.textContent = `Tuile (${cell.x},${cell.y})\n\nStatus : Inexploitable`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!cell.hasPlanet) {
|
if (!cell.hasPlanet) {
|
||||||
hint.textContent = `(${cell.x},${cell.y}) Vide`;
|
hint.textContent = `(${cell.x},${cell.y}) Vide`;
|
||||||
details.textContent = `Cell (${cell.x},${cell.y})\n\nStatus : Vide`;
|
details.textContent = `Tuile (${cell.x},${cell.y})\n\nStatus : Vide`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hint.textContent = `(${cell.x},${cell.y}) Planète présente`;
|
hint.textContent = `(${cell.x},${cell.y}) Planète présente`;
|
||||||
details.textContent = `Cell (${cell.x},${cell.y})\n\nStatus : Planète\n\n${formatPlanet(cell.planet)}`;
|
details.textContent = `Tuile (${cell.x},${cell.y})\n\nStatus : Planète\n\n${formatPlanet(cell.planet)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function showLocalSelection(x, y) {
|
function showLocalSelection(x, y) {
|
||||||
@@ -594,13 +605,13 @@ function showLocalSelection(x, y) {
|
|||||||
if (!isOwnTile(k)) return;
|
if (!isOwnTile(k)) return;
|
||||||
if (!isExploitable(x, y)) {
|
if (!isExploitable(x, y)) {
|
||||||
hint.textContent = `(${x},${y}) Inexploitable`;
|
hint.textContent = `(${x},${y}) Inexploitable`;
|
||||||
details.textContent = `Cell (${x},${y})\n\nStatus : Inexploitable`;
|
details.textContent = `Tuile (${x},${y})\n\nStatus : Inexploitable`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const meta = cellMeta(k);
|
const meta = cellMeta(k);
|
||||||
if (!meta?.hasPlanet) {
|
if (!meta?.hasPlanet) {
|
||||||
hint.textContent = `(${x},${y}) Vide`;
|
hint.textContent = `(${x},${y}) Vide`;
|
||||||
details.textContent = `Cell (${x},${y})\n\nStatus : Vide`;
|
details.textContent = `Tuile (${x},${y})\n\nStatus : Vide`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let planet = meta.planet;
|
let planet = meta.planet;
|
||||||
@@ -609,7 +620,7 @@ function showLocalSelection(x, y) {
|
|||||||
planet = generatePlanet(mulberry32(h));
|
planet = generatePlanet(mulberry32(h));
|
||||||
}
|
}
|
||||||
hint.textContent = `(${x},${y}) Planète présente`;
|
hint.textContent = `(${x},${y}) Planète présente`;
|
||||||
details.textContent = `Cell (${x},${y})\n\nStatus : Planète\n\n${formatPlanet(planet)}`;
|
details.textContent = `Tuile (${x},${y})\n\nStatus : Planète\n\n${formatPlanet(planet)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Canvas click handler ──────────────────────────────────────────────────────
|
// ── Canvas click handler ──────────────────────────────────────────────────────
|
||||||
@@ -684,6 +695,7 @@ export async function refreshFromServer() {
|
|||||||
}
|
}
|
||||||
await fetchGridForSeed(seedStr);
|
await fetchGridForSeed(seedStr);
|
||||||
await fetchAndApplyScores();
|
await fetchAndApplyScores();
|
||||||
|
await fetchAndApplyActivePlayers();
|
||||||
updateEconomyDisplay();
|
updateEconomyDisplay();
|
||||||
draw();
|
draw();
|
||||||
refreshCursorFromLast();
|
refreshCursorFromLast();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
fetchConfig,
|
fetchConfig,
|
||||||
fetchGridForSeed,
|
fetchGridForSeed,
|
||||||
fetchAndApplyScores,
|
fetchAndApplyScores,
|
||||||
|
fetchAndApplyActivePlayers,
|
||||||
updateEconomyDisplay,
|
updateEconomyDisplay,
|
||||||
loadEconScores,
|
loadEconScores,
|
||||||
loadVictoryPoints,
|
loadVictoryPoints,
|
||||||
@@ -54,6 +55,7 @@ function scheduleScorePoll() {
|
|||||||
clearTimeout(scorePollTimer);
|
clearTimeout(scorePollTimer);
|
||||||
scorePollTimer = window.setTimeout(async () => {
|
scorePollTimer = window.setTimeout(async () => {
|
||||||
await fetchAndApplyScores();
|
await fetchAndApplyScores();
|
||||||
|
await fetchAndApplyActivePlayers();
|
||||||
await loadEconScores();
|
await loadEconScores();
|
||||||
await loadElementBonus();
|
await loadElementBonus();
|
||||||
scheduleScorePoll();
|
scheduleScorePoll();
|
||||||
@@ -101,6 +103,7 @@ async function boot() {
|
|||||||
await fetchConfig();
|
await fetchConfig();
|
||||||
await fetchGridForSeed(seedStr);
|
await fetchGridForSeed(seedStr);
|
||||||
await fetchAndApplyScores();
|
await fetchAndApplyScores();
|
||||||
|
await fetchAndApplyActivePlayers();
|
||||||
await loadEconScores();
|
await loadEconScores();
|
||||||
await loadVictoryPoints();
|
await loadVictoryPoints();
|
||||||
await loadDbInfo();
|
await loadDbInfo();
|
||||||
|
|||||||
@@ -134,16 +134,23 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.authTeamBadge {
|
.authTeamBadge {
|
||||||
display: block;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 2px solid rgba(255, 255, 255, 0.1);
|
border: 2px solid rgba(255, 255, 255, 0.1);
|
||||||
text-align: center;
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
transition: border-color 0.15s, background 0.15s;
|
transition: border-color 0.15s, background 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.authTeamLogo {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.authTeamBadge--blue {
|
.authTeamBadge--blue {
|
||||||
color: rgba(90, 200, 255, 0.9);
|
color: rgba(90, 200, 255, 0.9);
|
||||||
}
|
}
|
||||||
@@ -192,6 +199,18 @@ body {
|
|||||||
background: rgba(113, 199, 255, 0.28);
|
background: rgba(113, 199, 255, 0.28);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.authTeamCount {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 4px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authTeamCount--blue { color: rgba(90, 200, 255, 0.9); }
|
||||||
|
.authTeamCount--red { color: rgba(220, 75, 85, 0.9); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ── Score board ──────────────────────────────────────────────────────────── */
|
/* ── Score board ──────────────────────────────────────────────────────────── */
|
||||||
@@ -239,6 +258,26 @@ body {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.teamLogoWrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teamPlayerCount {
|
||||||
|
font-size: 9px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
opacity: 0.75;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teamPlayerCount--blue { color: rgba(90, 200, 255, 0.9); }
|
||||||
|
.teamPlayerCount--red { color: rgba(220, 75, 85, 0.9); }
|
||||||
|
|
||||||
.scoreTeam {
|
.scoreTeam {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -273,3 +273,17 @@ export async function getScores(worldSeed) {
|
|||||||
);
|
);
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Active player counts (players who have played in the current epoch) ───────
|
||||||
|
|
||||||
|
export async function getActivePlayerCounts(worldSeed) {
|
||||||
|
const { rows } = await pool.query(
|
||||||
|
`SELECT team, COUNT(DISTINCT user_id)::int AS count
|
||||||
|
FROM user_cooldowns WHERE world_seed = $1
|
||||||
|
GROUP BY team`,
|
||||||
|
[worldSeed]
|
||||||
|
);
|
||||||
|
const result = { blue: 0, red: 0 };
|
||||||
|
for (const row of rows) result[row.team] = row.count;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,3 +43,12 @@ export async function getUserById(id) {
|
|||||||
);
|
);
|
||||||
return rows[0] ?? null;
|
return rows[0] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getTeamPlayerCounts() {
|
||||||
|
const { rows } = await usersPool.query(
|
||||||
|
`SELECT team, COUNT(*)::int AS count FROM users GROUP BY team`
|
||||||
|
);
|
||||||
|
const result = { blue: 0, red: 0 };
|
||||||
|
for (const row of rows) result[row.team] = row.count;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import express from "express";
|
|||||||
import bcrypt from "bcryptjs";
|
import bcrypt from "bcryptjs";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { JWT_SECRET, authMiddleware } from "../middleware/auth.js";
|
import { JWT_SECRET, authMiddleware } from "../middleware/auth.js";
|
||||||
import { createUser, getUserByUsername, getUserById } from "../db/usersDb.js";
|
import { createUser, getUserByUsername, getUserById, getTeamPlayerCounts } from "../db/usersDb.js";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -67,6 +67,17 @@ router.post("/login", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// GET /api/auth/player-counts
|
||||||
|
router.get("/player-counts", async (_req, res) => {
|
||||||
|
try {
|
||||||
|
const counts = await getTeamPlayerCounts();
|
||||||
|
return res.json(counts);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return res.status(500).json({ error: "database_error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// GET /api/auth/me
|
// GET /api/auth/me
|
||||||
router.get("/me", authMiddleware, async (req, res) => {
|
router.get("/me", authMiddleware, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
setElementBonus,
|
setElementBonus,
|
||||||
getDbCreatedAt,
|
getDbCreatedAt,
|
||||||
getVictoryPoints,
|
getVictoryPoints,
|
||||||
|
getActivePlayerCounts,
|
||||||
} from "../db/gameDb.js";
|
} from "../db/gameDb.js";
|
||||||
import { computeCell, rowToCellPayload } from "../helpers/cell.js";
|
import { computeCell, rowToCellPayload } from "../helpers/cell.js";
|
||||||
|
|
||||||
@@ -246,6 +247,18 @@ router.get("/victory-points", async (_req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// GET /api/active-players
|
||||||
|
router.get("/active-players", async (_req, res) => {
|
||||||
|
try {
|
||||||
|
const worldSeed = await ensureSeedEpoch();
|
||||||
|
const counts = await getActivePlayerCounts(worldSeed);
|
||||||
|
res.json(counts);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
res.status(500).json({ error: "database_error" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// GET /api/scores
|
// GET /api/scores
|
||||||
router.get("/scores", async (_req, res) => {
|
router.get("/scores", async (_req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user