Service Library - gameplay
This document provides a complete reference of the custom code library for the gameplay service. It includes all library functions, edge functions with their REST endpoints, templates, and assets.
Library Functions
Library functions are reusable modules available to all business APIs and other custom code within the service via require("lib/<moduleName>").
processGameResult.js
const { interserviceCall } = require("serviceCommon");
/**
* Mindbricks interservice calls return the same envelope as REST (e.g. { playerStats: {...} }).
* Unwrap so we read id, eloRating, wins, etc. from the actual row.
*/
function unwrapPlayerStats(res) {
if (res == null || typeof res !== "object") return null;
const inner = res.playerStats;
if (inner && typeof inner === "object" && inner.id) return inner;
if (res.id && res.userId) return res;
return null;
}
/**
* Processes a completed/terminated chess game result:
* - Computes ELO changes for both players
* - Calls leaderboard service to update playerStats for registered (non-guest) players
* - Calls leaderboard service to update leaderboardEntry eloRating and rank
*
* @param {object} context - The API context (this)
*/
module.exports = async function processGameResult(context) {
const game = context.chessGame;
const updatedFields = context;
const status = updatedFields.status || game.status;
const result = updatedFields.result || game.result;
if (!["completed", "terminated"].includes(status) || !result) {
return null;
}
if (result === "aborted") {
return null;
}
const playerWhiteId = game.playerWhiteId;
const playerBlackId = game.playerBlackId;
const isWhiteGuest = game.guestPlayerWhite === true;
const isBlackGuest = game.guestPlayerBlack === true;
let whiteOutcome;
let blackOutcome;
if (result === "whiteWin") {
whiteOutcome = "win";
blackOutcome = "loss";
} else if (result === "blackWin") {
whiteOutcome = "loss";
blackOutcome = "win";
} else if (result === "draw") {
whiteOutcome = "draw";
blackOutcome = "draw";
} else {
return null;
}
let whiteStats = null;
let blackStats = null;
if (!isWhiteGuest && playerWhiteId) {
try {
const raw = await interserviceCall("leaderboard", "getPlayerStats", { userId: playerWhiteId });
whiteStats = unwrapPlayerStats(raw);
} catch (e) {
/* player may not have stats yet */
}
}
if (!isBlackGuest && playerBlackId) {
try {
const raw = await interserviceCall("leaderboard", "getPlayerStats", { userId: playerBlackId });
blackStats = unwrapPlayerStats(raw);
} catch (e) {
/* player may not have stats yet */
}
}
const whiteElo = whiteStats ? (Number(whiteStats.eloRating) || 1200) : 1200;
const blackElo = blackStats ? (Number(blackStats.eloRating) || 1200) : 1200;
const K = 32;
const expectedWhite = 1 / (1 + Math.pow(10, (blackElo - whiteElo) / 400));
const expectedBlack = 1 / (1 + Math.pow(10, (whiteElo - blackElo) / 400));
let actualWhite;
let actualBlack;
if (result === "whiteWin") {
actualWhite = 1;
actualBlack = 0;
} else if (result === "blackWin") {
actualWhite = 0;
actualBlack = 1;
} else {
actualWhite = 0.5;
actualBlack = 0.5;
}
const newWhiteElo = Math.round(whiteElo + K * (actualWhite - expectedWhite));
const newBlackElo = Math.round(blackElo + K * (actualBlack - expectedBlack));
const now = new Date().toISOString();
function buildUpdatePayload(stats, outcome, newElo) {
const wins = (stats ? stats.wins : 0) + (outcome === "win" ? 1 : 0);
const losses = (stats ? stats.losses : 0) + (outcome === "loss" ? 1 : 0);
const draws = (stats ? stats.draws : 0) + (outcome === "draw" ? 1 : 0);
const totalGames = wins + losses + draws;
let streak = stats ? stats.streak : 0;
if (outcome === "win") streak = streak > 0 ? streak + 1 : 1;
else if (outcome === "loss") streak = streak < 0 ? streak - 1 : -1;
else streak = 0;
return { eloRating: newElo, totalGames, wins, losses, draws, streak, lastGameAt: now };
}
if (!isWhiteGuest && playerWhiteId && whiteStats) {
try {
const payload = buildUpdatePayload(whiteStats, whiteOutcome, newWhiteElo);
await interserviceCall("leaderboard", "updatePlayerStats", { id: whiteStats.id, ...payload });
} catch (e) {
console.error("Failed to update white player stats:", e.message);
}
}
if (!isBlackGuest && playerBlackId && blackStats) {
try {
const payload = buildUpdatePayload(blackStats, blackOutcome, newBlackElo);
await interserviceCall("leaderboard", "updatePlayerStats", { id: blackStats.id, ...payload });
} catch (e) {
console.error("Failed to update black player stats:", e.message);
}
}
return { whiteElo: newWhiteElo, blackElo: newBlackElo, result };
};
This document was generated from the service library configuration and should be kept in sync with design changes.