feat: 참가 신청 기능 추가 및 관련 로직 수정
This commit is contained in:
@@ -223,6 +223,50 @@ export const tournamentRouter = router({
|
|||||||
|
|
||||||
return { state, totals, myTotals, totalAmount, myAmount };
|
return { state, totals, myTotals, totalAmount, myAmount };
|
||||||
}),
|
}),
|
||||||
|
join: authedProcedure.mutation(async ({ ctx }) => {
|
||||||
|
const userId = ctx.auth?.user.id;
|
||||||
|
if (!userId) {
|
||||||
|
throw new TRPCError({ code: 'UNAUTHORIZED' });
|
||||||
|
}
|
||||||
|
const store = new TournamentStore(ctx.redis, buildTournamentKeys(ctx.profile.name));
|
||||||
|
const state = await store.getState();
|
||||||
|
if (!state || state.stage !== 1) {
|
||||||
|
throw new TRPCError({ code: 'BAD_REQUEST', message: '참가 신청 기간이 아닙니다.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const participants = await store.getParticipants();
|
||||||
|
|
||||||
|
const general = await ctx.db.general.findFirst({
|
||||||
|
where: { userId },
|
||||||
|
select: { id: true, name: true, leadership: true, strength: true, intel: true, meta: true },
|
||||||
|
});
|
||||||
|
if (!general) {
|
||||||
|
throw new TRPCError({ code: 'NOT_FOUND', message: '장수 정보를 찾을 수 없습니다.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const already = participants.find((entry) => entry.id === general.id);
|
||||||
|
if (already) {
|
||||||
|
return { ok: true, count: participants.length };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (participants.length >= 64) {
|
||||||
|
throw new TRPCError({ code: 'BAD_REQUEST', message: '참가 인원이 가득 찼습니다.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const meta = asRecord(general.meta);
|
||||||
|
const level = typeof meta.explevel === 'number' ? meta.explevel : 0;
|
||||||
|
const next = participants.concat({
|
||||||
|
id: general.id,
|
||||||
|
name: general.name,
|
||||||
|
leadership: general.leadership,
|
||||||
|
strength: general.strength,
|
||||||
|
intel: general.intel,
|
||||||
|
level,
|
||||||
|
});
|
||||||
|
|
||||||
|
await store.setParticipants(next);
|
||||||
|
return { ok: true, count: next.length };
|
||||||
|
}),
|
||||||
placeBet: authedProcedure
|
placeBet: authedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { asRecord, createTournamentRng } from '@sammo-ts/common';
|
import { createTournamentRng } from '@sammo-ts/common';
|
||||||
import { resolveTournamentBattle, TournamentType } from '@sammo-ts/logic';
|
import { resolveTournamentBattle, TournamentType } from '@sammo-ts/logic';
|
||||||
import {
|
import {
|
||||||
createGamePostgresConnector,
|
createGamePostgresConnector,
|
||||||
@@ -419,61 +419,6 @@ const applyPreBattleStage = async (
|
|||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolveParticipantOrder = (type: TournamentType): Array<
|
|
||||||
{ leadership: 'desc' } | { strength: 'desc' } | { intel: 'desc' } | { id: 'asc' }
|
|
||||||
> => {
|
|
||||||
switch (type) {
|
|
||||||
case TournamentType.LEADERSHIP:
|
|
||||||
return [{ leadership: 'desc' }, { id: 'asc' }];
|
|
||||||
case TournamentType.STRENGTH:
|
|
||||||
return [{ strength: 'desc' }, { id: 'asc' }];
|
|
||||||
case TournamentType.INTEL:
|
|
||||||
return [{ intel: 'desc' }, { id: 'asc' }];
|
|
||||||
case TournamentType.TOTAL:
|
|
||||||
default:
|
|
||||||
return [{ leadership: 'desc' }, { strength: 'desc' }, { intel: 'desc' }, { id: 'asc' }];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ensureParticipants = async (
|
|
||||||
store: TournamentStore,
|
|
||||||
prisma: ReturnType<typeof createGamePostgresConnector>['prisma'],
|
|
||||||
state: TournamentState,
|
|
||||||
limit: number
|
|
||||||
): Promise<void> => {
|
|
||||||
const participants = await store.getParticipants();
|
|
||||||
if (participants.length > 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const generals = await prisma.general.findMany({
|
|
||||||
orderBy: resolveParticipantOrder(state.type),
|
|
||||||
take: limit,
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
name: true,
|
|
||||||
leadership: true,
|
|
||||||
strength: true,
|
|
||||||
intel: true,
|
|
||||||
meta: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const snapshot = generals.map((general) => {
|
|
||||||
const meta = asRecord(general.meta);
|
|
||||||
const level = typeof meta.explevel === 'number' ? meta.explevel : 0;
|
|
||||||
return {
|
|
||||||
id: general.id,
|
|
||||||
name: general.name,
|
|
||||||
leadership: general.leadership,
|
|
||||||
strength: general.strength,
|
|
||||||
intel: general.intel,
|
|
||||||
level,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
await store.setParticipants(snapshot);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const runTournamentWorker = async (): Promise<void> => {
|
export const runTournamentWorker = async (): Promise<void> => {
|
||||||
const config = resolveGameApiConfigFromEnv();
|
const config = resolveGameApiConfigFromEnv();
|
||||||
@@ -514,9 +459,6 @@ export const runTournamentWorker = async (): Promise<void> => {
|
|||||||
const worldState = await postgres.prisma.worldState.findFirst();
|
const worldState = await postgres.prisma.worldState.findFirst();
|
||||||
const baseSeed = (worldState?.meta as Record<string, unknown> | null)?.hiddenSeed ?? 'tournament';
|
const baseSeed = (worldState?.meta as Record<string, unknown> | null)?.hiddenSeed ?? 'tournament';
|
||||||
let nextState = state;
|
let nextState = state;
|
||||||
if (isPreBattleStage(state.stage) && (state.stage === 1 || state.stage === 3 || state.stage === 4)) {
|
|
||||||
await ensureParticipants(store, postgres.prisma, state, 64);
|
|
||||||
}
|
|
||||||
if (isBattleStage(state.stage)) {
|
if (isBattleStage(state.stage)) {
|
||||||
nextState = await applyBattle(store, state, String(baseSeed));
|
nextState = await applyBattle(store, state, String(baseSeed));
|
||||||
} else if (isPreBattleStage(state.stage)) {
|
} else if (isPreBattleStage(state.stage)) {
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ const placeBet = async () => {
|
|||||||
|
|
||||||
const toggleJoin = async () => {
|
const toggleJoin = async () => {
|
||||||
try {
|
try {
|
||||||
await trpc.general.setMySetting.mutate({ tnmt: 1 });
|
await trpc.tournament.join.mutate();
|
||||||
lastActionMessage.value = '참가 신청이 반영되었습니다.';
|
lastActionMessage.value = '참가 신청이 반영되었습니다.';
|
||||||
await loadTournament();
|
await loadTournament();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user