feat(leaderboard): missions & guilds leaderboard (#1338)

Reviewed-on: OpenWF/SpaceNinjaServer#1338
Reviewed-by: Sainan <sainan@calamity.inc>
Co-authored-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
Co-committed-by: AMelonInsideLemon <166175391+AMelonInsideLemon@users.noreply.github.com>
This commit is contained in:
AMelonInsideLemon 2025-03-26 14:21:22 -07:00 committed by Sainan
parent 0f7866a575
commit 049f709713
5 changed files with 59 additions and 11 deletions

View File

@ -6,7 +6,14 @@ export const leaderboardController: RequestHandler = async (req, res) => {
logger.debug(`data provided to ${req.path}: ${String(req.body)}`); logger.debug(`data provided to ${req.path}: ${String(req.body)}`);
const payload = JSON.parse(String(req.body)) as ILeaderboardRequest; const payload = JSON.parse(String(req.body)) as ILeaderboardRequest;
res.json({ res.json({
results: await getLeaderboard(payload.field, payload.before, payload.after, payload.guildId, payload.pivotId) results: await getLeaderboard(
payload.field,
payload.before,
payload.after,
payload.guildId,
payload.pivotId,
payload.guildTier
)
}); });
}; };
@ -16,4 +23,5 @@ interface ILeaderboardRequest {
after: number; after: number;
guildId?: string; guildId?: string;
pivotId?: string; pivotId?: string;
guildTier?: number;
} }

View File

@ -8,7 +8,8 @@ const leaderboardEntrySchema = new Schema<ILeaderboardEntryDatabase>(
displayName: { type: String, required: true }, displayName: { type: String, required: true },
score: { type: Number, required: true }, score: { type: Number, required: true },
guildId: Schema.Types.ObjectId, guildId: Schema.Types.ObjectId,
expiry: { type: Date, required: true } expiry: { type: Date, required: true },
guildTier: Number
}, },
{ id: false } { id: false }
); );

View File

@ -1,14 +1,15 @@
import { Guild } from "../models/guildModel";
import { Leaderboard, TLeaderboardEntryDocument } from "../models/leaderboardModel"; import { Leaderboard, TLeaderboardEntryDocument } from "../models/leaderboardModel";
import { ILeaderboardEntryClient } from "../types/leaderboardTypes"; import { ILeaderboardEntryClient } from "../types/leaderboardTypes";
export const submitLeaderboardScore = async ( export const submitLeaderboardScore = async (
schedule: "weekly" | "daily",
leaderboard: string, leaderboard: string,
ownerId: string, ownerId: string,
displayName: string, displayName: string,
score: number, score: number,
guildId?: string guildId?: string
): Promise<void> => { ): Promise<void> => {
const schedule = leaderboard.split(".")[0] as "daily" | "weekly";
let expiry: Date; let expiry: Date;
if (schedule == "daily") { if (schedule == "daily") {
expiry = new Date(Math.trunc(Date.now() / 86400000) * 86400000 + 86400000); expiry = new Date(Math.trunc(Date.now() / 86400000) * 86400000 + 86400000);
@ -21,10 +22,18 @@ export const submitLeaderboardScore = async (
expiry = new Date(weekEnd); expiry = new Date(weekEnd);
} }
await Leaderboard.findOneAndUpdate( await Leaderboard.findOneAndUpdate(
{ leaderboard, ownerId }, { leaderboard: `${schedule}.accounts.${leaderboard}`, ownerId },
{ $max: { score }, $set: { displayName, guildId, expiry } }, { $max: { score }, $set: { displayName, guildId, expiry } },
{ upsert: true } { upsert: true }
); );
if (guildId) {
const guild = (await Guild.findById(guildId, "Name Tier"))!;
await Leaderboard.findOneAndUpdate(
{ leaderboard: `${schedule}.guilds.${leaderboard}`, ownerId: guildId },
{ $max: { score }, $set: { displayName: guild.Name, guildTier: guild.Tier, expiry } },
{ upsert: true }
);
}
}; };
export const getLeaderboard = async ( export const getLeaderboard = async (
@ -32,12 +41,16 @@ export const getLeaderboard = async (
before: number, before: number,
after: number, after: number,
guildId?: string, guildId?: string,
pivotId?: string pivotId?: string,
guildTier?: number
): Promise<ILeaderboardEntryClient[]> => { ): Promise<ILeaderboardEntryClient[]> => {
const filter: { leaderboard: string; guildId?: string } = { leaderboard }; const filter: { leaderboard: string; guildId?: string; guildTier?: number } = { leaderboard };
if (guildId) { if (guildId) {
filter.guildId = guildId; filter.guildId = guildId;
} }
if (guildTier) {
filter.guildTier = guildTier;
}
let entries: TLeaderboardEntryDocument[]; let entries: TLeaderboardEntryDocument[];
let r: number; let r: number;

View File

@ -286,6 +286,15 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
} else { } else {
playerStats.Missions.push({ type: type, highScore }); playerStats.Missions.push({ type: type, highScore });
} }
await submitLeaderboardScore(
"weekly",
type,
accountOwnerId,
payload.displayName,
highScore,
payload.guildId
);
break;
} }
break; break;
@ -304,27 +313,42 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
} }
await submitLeaderboardScore( await submitLeaderboardScore(
"daily.accounts." + race, "daily",
race,
accountOwnerId, accountOwnerId,
payload.displayName, payload.displayName,
highScore highScore,
payload.guildId
); );
} }
break; break;
case "ZephyrScore": case "ZephyrScore":
case "SentinelGameScore":
case "CaliberChicksScore": case "CaliberChicksScore":
playerStats[category] ??= 0; playerStats[category] ??= 0;
if (data > playerStats[category]) playerStats[category] = data as number; if (data > playerStats[category]) playerStats[category] = data as number;
break; break;
case "SentinelGameScore":
playerStats[category] ??= 0;
if (data > playerStats[category]) playerStats[category] = data as number;
await submitLeaderboardScore(
"weekly",
category,
accountOwnerId,
payload.displayName,
data as number,
payload.guildId
);
break;
case "DojoObstacleScore": case "DojoObstacleScore":
playerStats[category] ??= 0; playerStats[category] ??= 0;
if (data > playerStats[category]) playerStats[category] = data as number; if (data > playerStats[category]) playerStats[category] = data as number;
await submitLeaderboardScore( await submitLeaderboardScore(
"weekly.accounts." + category, "weekly",
category,
accountOwnerId, accountOwnerId,
payload.displayName, payload.displayName,
data as number, data as number,
@ -350,7 +374,8 @@ export const updateStats = async (accountOwnerId: string, payload: IStatsUpdate)
} }
if (data > playerStats[category]) playerStats[category] = data as number; if (data > playerStats[category]) playerStats[category] = data as number;
await submitLeaderboardScore( await submitLeaderboardScore(
"weekly.accounts." + category, "weekly",
category,
accountOwnerId, accountOwnerId,
payload.displayName, payload.displayName,
data as number data as number

View File

@ -7,6 +7,7 @@ export interface ILeaderboardEntryDatabase {
score: number; score: number;
guildId?: Types.ObjectId; guildId?: Types.ObjectId;
expiry: Date; expiry: Date;
guildTier?: number;
} }
export interface ILeaderboardEntryClient { export interface ILeaderboardEntryClient {