75 lines
2.9 KiB
TypeScript
75 lines
2.9 KiB
TypeScript
import { randomInt } from "node:crypto";
|
|
import { content } from "@deadhead/content";
|
|
import { routes } from "@deadhead/schema";
|
|
import { realToGame } from "@deadhead/sim";
|
|
import { fromNodeHeaders } from "better-auth/node";
|
|
import type { FastifyInstance } from "fastify";
|
|
import { acceptDeal } from "../game/deal";
|
|
import { claimGoal } from "../game/goals";
|
|
import { repayLoan } from "../game/loan";
|
|
import { createPlayer } from "../game/new-player";
|
|
import { loadPlayerState } from "../game/player-state";
|
|
import { acknowledgeSummaries } from "../game/summaries";
|
|
import { ApiError } from "../http/errors";
|
|
import { registerRoute } from "../http/route";
|
|
import type { AppContext } from "./context";
|
|
import { withPlayer } from "./with-player";
|
|
|
|
/** Server time, guest sign-up and player state. */
|
|
export function registerPlayerRoutes(app: FastifyInstance, ctx: AppContext): void {
|
|
const { db, auth, clock, options } = ctx;
|
|
|
|
registerRoute(app, routes.getServerTime, options, async () => {
|
|
const realTime = clock();
|
|
return { realTime, gameTime: realToGame(realTime, content.config) };
|
|
});
|
|
|
|
registerRoute(app, routes.createGuest, options, async (_input, { request, reply }) => {
|
|
const signIn = await auth.api.signInAnonymous({
|
|
headers: fromNodeHeaders(request.headers),
|
|
returnHeaders: true,
|
|
});
|
|
if (!signIn.response) throw new ApiError(500, "internal-error", "Could not create a guest.");
|
|
|
|
const { playerId } = await createPlayer(db, {
|
|
userId: signIn.response.user.id,
|
|
// A 32-bit unsigned seed (decision 0.5.10).
|
|
worldSeed: randomInt(0, 2 ** 32),
|
|
now: clock(),
|
|
});
|
|
// Pass Better Auth's session cookie through to the client.
|
|
reply.header("set-cookie", signIn.headers.getSetCookie());
|
|
return { playerId, state: await loadPlayerState(db, playerId) };
|
|
});
|
|
|
|
// Lazy resolution on read (Step 6.1): apply every arrival before returning state.
|
|
registerRoute(app, routes.getPlayerState, options, async (_input, { playerId }) =>
|
|
withPlayer(ctx, playerId, (tx) => loadPlayerState(tx, playerId)),
|
|
);
|
|
|
|
registerRoute(app, routes.acknowledgeSummaries, options, async (input, { playerId }) =>
|
|
withPlayer(ctx, playerId, async (tx, now) => ({
|
|
acknowledged: await acknowledgeSummaries(tx, playerId, input.dispatchIds, now),
|
|
})),
|
|
);
|
|
|
|
registerRoute(app, routes.acceptDeal, options, async (_input, { playerId }) =>
|
|
withPlayer(ctx, playerId, async (tx, now) => {
|
|
await acceptDeal(tx, playerId, now);
|
|
return loadPlayerState(tx, playerId);
|
|
}),
|
|
);
|
|
|
|
registerRoute(app, routes.repayLoan, options, async (input, { playerId }) =>
|
|
withPlayer(ctx, playerId, async (tx) => ({
|
|
company: await repayLoan(tx, playerId, input.amount),
|
|
})),
|
|
);
|
|
|
|
registerRoute(app, routes.claimGoal, options, async (input, { playerId }) =>
|
|
withPlayer(ctx, playerId, async (tx, now) => ({
|
|
company: await claimGoal(tx, playerId, input.goalId, now),
|
|
})),
|
|
);
|
|
}
|