feat: Zod를 사용한 월드 상태 구성 및 메타 타입 추가, 사용자 ID 처리 개선
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { z } from 'zod';
|
||||
import type { GameSessionTokenPayload } from '@sammo-ts/common';
|
||||
import type { DatabaseClient as InfraDatabaseClient, RedisConnector } from '@sammo-ts/infra';
|
||||
|
||||
@@ -10,6 +11,21 @@ export interface GameProfile {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const zWorldStateConfig = z.object({
|
||||
maxUserCnt: z.number().optional(),
|
||||
fictionMode: z.string().optional(),
|
||||
});
|
||||
export type WorldStateConfig = z.infer<typeof zWorldStateConfig>;
|
||||
|
||||
export const zWorldStateMeta = z.object({
|
||||
starttime: z.string().optional(),
|
||||
opentime: z.string().optional(),
|
||||
turntime: z.string().optional(),
|
||||
otherTextInfo: z.string().optional(),
|
||||
isUnited: z.number().optional(),
|
||||
});
|
||||
export type WorldStateMeta = z.infer<typeof zWorldStateMeta>;
|
||||
|
||||
export interface WorldStateRow {
|
||||
scenarioCode: string;
|
||||
currentYear: number;
|
||||
@@ -22,7 +38,7 @@ export interface WorldStateRow {
|
||||
|
||||
export interface GeneralRow {
|
||||
id: number;
|
||||
userId: number | null;
|
||||
userId: string | null;
|
||||
name: string;
|
||||
nationId: number;
|
||||
cityId: number;
|
||||
@@ -49,6 +65,7 @@ export interface GeneralRow {
|
||||
atmos: number;
|
||||
age: number;
|
||||
npcState: number;
|
||||
picture: string | null;
|
||||
meta: unknown;
|
||||
}
|
||||
|
||||
|
||||
+21
-14
@@ -1,7 +1,8 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { WorldStateRow } from './context.js';
|
||||
import { zWorldStateConfig, zWorldStateMeta } from './context.js';
|
||||
import type { GameApiContext, WorldStateRow } from './context.js';
|
||||
import { authedProcedure, procedure, router } from './trpc.js';
|
||||
import { buildTurnCommandTable } from './turns/commandTable.js';
|
||||
import {
|
||||
@@ -67,12 +68,12 @@ const toWorldStateSnapshot = (row: WorldStateRow) => ({
|
||||
updatedAt: row.updatedAt.toISOString(),
|
||||
});
|
||||
|
||||
const getMyGeneral = async (ctx: { db: any, auth: any }) => {
|
||||
const getMyGeneral = async (ctx: Pick<GameApiContext, 'db' | 'auth'>) => {
|
||||
if (!ctx.auth?.user.id) {
|
||||
throw new TRPCError({ code: 'UNAUTHORIZED' });
|
||||
}
|
||||
const general = await ctx.db.general.findFirst({
|
||||
where: { userId: parseInt(ctx.auth.user.id) },
|
||||
where: { userId: ctx.auth.user.id },
|
||||
});
|
||||
if (!general) {
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'General not found' });
|
||||
@@ -90,14 +91,20 @@ export const appRouter = router({
|
||||
}),
|
||||
lobby: router({
|
||||
info: procedure.query(async ({ ctx }) => {
|
||||
const worldState = await ctx.db.worldState.findFirst();
|
||||
if (!worldState) {
|
||||
const rawWorldState = await ctx.db.worldState.findFirst();
|
||||
if (!rawWorldState) {
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'World state not found',
|
||||
});
|
||||
}
|
||||
|
||||
const worldState = {
|
||||
...rawWorldState,
|
||||
config: zWorldStateConfig.parse(rawWorldState.config),
|
||||
meta: zWorldStateMeta.parse(rawWorldState.meta),
|
||||
};
|
||||
|
||||
const userCnt = await ctx.db.general.count({ where: { npcState: 0 } });
|
||||
const npcCnt = await ctx.db.general.count({ where: { npcState: { gt: 0 } } });
|
||||
const nationCnt = await ctx.db.nation.count({ where: { level: { gt: 0 } } });
|
||||
@@ -111,8 +118,8 @@ export const appRouter = router({
|
||||
});
|
||||
if (general) {
|
||||
myGeneral = {
|
||||
name: (general as any).name,
|
||||
picture: (general as any).picture,
|
||||
name: general.name,
|
||||
picture: general.picture,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -121,16 +128,16 @@ export const appRouter = router({
|
||||
year: worldState.currentYear,
|
||||
month: worldState.currentMonth,
|
||||
userCnt,
|
||||
maxUserCnt: (worldState.config as any).maxUserCnt ?? 500,
|
||||
maxUserCnt: worldState.config.maxUserCnt ?? 500,
|
||||
npcCnt,
|
||||
nationCnt,
|
||||
turnTerm: worldState.tickSeconds / 60,
|
||||
fictionMode: (worldState.config as any).fictionMode ?? '사실',
|
||||
starttime: (worldState.meta as any).starttime ?? '',
|
||||
opentime: (worldState.meta as any).opentime ?? '',
|
||||
turntime: (worldState.meta as any).turntime ?? '',
|
||||
otherTextInfo: (worldState.meta as any).otherTextInfo ?? '',
|
||||
isUnited: (worldState.meta as any).isUnited ?? 0,
|
||||
fictionMode: worldState.config.fictionMode ?? '사실',
|
||||
starttime: worldState.meta.starttime ?? '',
|
||||
opentime: worldState.meta.opentime ?? '',
|
||||
turntime: worldState.meta.turntime ?? '',
|
||||
otherTextInfo: worldState.meta.otherTextInfo ?? '',
|
||||
isUnited: worldState.meta.isUnited ?? 0,
|
||||
myGeneral,
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -84,7 +84,7 @@ export const createGameApiServer = async () => {
|
||||
const token = extractBearerToken(req.headers.authorization);
|
||||
const auth = token ? tokenVerifier.verify(token) : null;
|
||||
return createGameApiContext({
|
||||
db: postgres.prisma as unknown as DatabaseClient,
|
||||
db: postgres.prisma,
|
||||
redis: redis.client,
|
||||
turnDaemon,
|
||||
battleSim,
|
||||
|
||||
Reference in New Issue
Block a user