Release v0.1.7 2026-07-12

This commit is contained in:
Warren H
2026-07-12 23:39:57 -04:00
parent 122f159b94
commit 77fd434226
31 changed files with 545 additions and 113 deletions
+38
View File
@@ -217,6 +217,14 @@ function syncLeaderboardStats(database, accountId, slotId, save) {
highest_round = excluded.highest_round,
updated_at = CURRENT_TIMESTAMP
`).run(accountId, slotId, highestRound);
const highestEndlessKills = normalizeNonNegativeInteger(save.stats?.highestRogueTrialsEndlessKills);
database.prepare(`
INSERT INTO rogue_trials_endless_records (account_id, slot_id, highest_boss_kills, updated_at)
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
ON CONFLICT(account_id, slot_id) DO UPDATE SET
highest_boss_kills = excluded.highest_boss_kills,
updated_at = CURRENT_TIMESTAMP
`).run(accountId, slotId, highestEndlessKills);
}
function writeSave(database, accountId, slotId, rawSave) {
@@ -324,6 +332,32 @@ function roguelikeLeaderboard(database, accountId, slotId) {
};
}
function rogueTrialsEndlessLeaderboard(database, accountId, slotId) {
const rows = database.prepare(`
WITH ranked AS (
SELECT
RANK() OVER (ORDER BY records.highest_boss_kills DESC) AS rank,
records.account_id AS accountId,
records.slot_id AS slotId,
records.highest_boss_kills AS highestBossKills,
accounts.username,
saves.hunter_name AS hunterName,
records.updated_at AS updatedAt
FROM rogue_trials_endless_records records
JOIN accounts ON accounts.id = records.account_id
JOIN hunter_saves saves ON saves.account_id = records.account_id AND saves.slot_id = records.slot_id
WHERE records.highest_boss_kills > 0
)
SELECT * FROM ranked ORDER BY highestBossKills DESC, updatedAt ASC, accountId ASC, slotId ASC
`).all();
const current = rows.find((row) => row.accountId === accountId && row.slotId === slotId) ?? null;
return {
kind: "rogue-trials-endless",
top: rows.slice(0, 5).map((row) => leaderboardEntry(row, "highestBossKills")),
current: current ? leaderboardEntry(current, "highestBossKills") : null,
};
}
export function createGameApiHandler(options = {}) {
const dataDirectory = resolve(options.dataDirectory ?? process.env.DATA_DIR ?? "data");
mkdirSync(dataDirectory, { recursive: true });
@@ -387,6 +421,10 @@ export function createGameApiHandler(options = {}) {
const slotId = validateSlotId(url.searchParams.get("slot"));
return sendJson(response, 200, roguelikeLeaderboard(database, session.accountId, slotId));
}
if (path === "/api/leaderboards/rogue-trials-endless" && request.method === "GET") {
const slotId = validateSlotId(url.searchParams.get("slot"));
return sendJson(response, 200, rogueTrialsEndlessLeaderboard(database, session.accountId, slotId));
}
return sendJson(response, 404, { error: "API route not found." });
} catch (error) {
const status = Number(error?.status) || 500;
+14 -4
View File
@@ -34,12 +34,12 @@ async function json(path, init = {}) {
return { response, body };
}
function save(slotId, hunterName, bulldromeKills, highestRoguelikeRound) {
function save(slotId, hunterName, bulldromeKills, highestRoguelikeRound, highestRogueTrialsEndlessKills) {
return {
schemaVersion: 5,
slotId,
hunterName,
stats: { bossKills: { bulldrome: bulldromeKills }, highestRoguelikeRound },
stats: { bossKills: { bulldrome: bulldromeKills }, highestRoguelikeRound, highestRogueTrialsEndlessKills },
};
}
@@ -62,13 +62,14 @@ test("accounts, server saves, and top-five plus current rankings work end to end
const token = registration.body.token;
const kills = 60 - index * 10;
const highestRound = 30 - index * 4;
const highestEndlessKills = 24 - index * 3;
const upload = await json("/api/saves/1", {
method: "PUT",
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify({ save: save(1, `Hero ${index}`, kills, highestRound) }),
body: JSON.stringify({ save: save(1, `Hero ${index}`, kills, highestRound, highestEndlessKills) }),
});
assert.equal(upload.response.status, 200);
players.push({ token, kills, highestRound });
players.push({ token, kills, highestRound, highestEndlessKills });
}
const current = players[5];
@@ -87,6 +88,15 @@ test("accounts, server saves, and top-five plus current rankings work end to end
assert.equal(rogueBoard.body.current.rank, 6);
assert.equal(rogueBoard.body.current.value, current.highestRound);
const endlessBoard = await json("/api/leaderboards/rogue-trials-endless?slot=1", {
headers: { Authorization: `Bearer ${current.token}` },
});
assert.equal(endlessBoard.body.kind, "rogue-trials-endless");
assert.equal(endlessBoard.body.top.length, 5);
assert.equal(endlessBoard.body.top[0].value, 24);
assert.equal(endlessBoard.body.current.rank, 6);
assert.equal(endlessBoard.body.current.value, current.highestEndlessKills);
const download = await json("/api/saves/1", {
headers: { Authorization: `Bearer ${current.token}` },
});