merge: 최신 main을 비용 기반 갱신 점수에 통합

This commit is contained in:
2026-08-17 11:10:43 +00:00
68 changed files with 2128 additions and 306 deletions
+20 -40
View File
@@ -7,6 +7,7 @@ import type { TournamentState } from '../../tournament/types.js';
import { TournamentStore } from '../../tournament/store.js';
import { buildTournamentKeys } from '../../tournament/keys.js';
import { assignManualApplicantGroup } from '../../tournament/workerHelpers.js';
import { accessAuthedProcedure, authedProcedure, router } from '../../trpc.js';
import { getMyGeneral } from '../shared/general.js';
import { loadCurrentGameTime } from '../../services/gameClock.js';
@@ -412,52 +413,31 @@ export const tournamentRouter = router({
});
}
const settingResult = await ctx.turnDaemon.requestCommand({
type: 'setMySetting',
generalId: general.id,
settings: { tnmt: 1 },
const meta = asRecord(general.meta);
const level = typeof meta.explevel === 'number' ? meta.explevel : 0;
const applicant = assignManualApplicantGroup({
state,
baseSeed: String(asRecord(worldState?.meta).hiddenSeed ?? 'tournament'),
current: participants,
applicant: {
id: general.id,
name: general.name,
leadership: general.leadership,
strength: general.strength,
intel: general.intel,
level,
},
});
if (!settingResult || settingResult.type !== 'setMySetting' || !settingResult.ok) {
const next = participants.concat(applicant);
try {
await store.setParticipants(next);
} catch (error) {
await ctx.turnDaemon.requestCommand({
type: 'adjustGeneralResources',
reason: 'tournamentJoinRollback',
adjustments: [{ generalId: general.id, goldDelta: develCost }],
});
throw new TRPCError({
code: 'BAD_REQUEST',
message:
settingResult && settingResult.type === 'setMySetting'
? (settingResult.reason ?? '요청에 실패했습니다.')
: 'Unexpected response',
});
}
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,
});
try {
await store.setParticipants(next);
} catch (error) {
await Promise.all([
ctx.turnDaemon.requestCommand({
type: 'adjustGeneralResources',
reason: 'tournamentJoinRollback',
adjustments: [{ generalId: general.id, goldDelta: develCost }],
}),
ctx.turnDaemon.requestCommand({
type: 'setMySetting',
generalId: general.id,
settings: { tnmt: 0 },
}),
]);
throw error;
}
return { ok: true, count: next.length };
+29 -14
View File
@@ -28,13 +28,14 @@ import {
repeatNationTurns,
setGeneralTurn,
setGeneralTurns,
setNationTurn,
setNationTurns,
setNationTurnAtCurrentPosition,
setNationTurnsAtCurrentPositions,
shiftGeneralTurns,
shiftNationTurns,
} from '../../turns/reservedTurns.js';
import { getOwnedGeneral } from '../shared/general.js';
import type { GameApiContext, GeneralRow, WorldStateRow } from '../../context.js';
import { buildRefGeneralTargetOptions } from '../../turns/commandTargets.js';
const zPushAmount = z
.number()
@@ -181,9 +182,15 @@ export const getTurnCommandTable = async (ctx: GameApiContext, generalId: number
orderBy: { id: 'asc' },
}),
ctx.db.general.findMany({
where: { npcState: { lt: 2 } },
select: { id: true, name: true, nationId: true, cityId: true },
orderBy: { id: 'asc' },
select: {
id: true,
name: true,
nationId: true,
cityId: true,
npcState: true,
officerLevel: true,
},
orderBy: [{ npcState: 'asc' }, { name: 'asc' }, { id: 'asc' }],
}),
environmentPromise,
loadBattleSimTraitOptions(),
@@ -192,7 +199,13 @@ export const getTurnCommandTable = async (ctx: GameApiContext, generalId: number
]);
const nationById = new Map(nations.map((entry) => [entry.id, entry]));
const cityById = new Map(cities.map((entry) => [entry.id, entry]));
const generalTargetOptions = buildRefGeneralTargetOptions({
actorId: general.id,
actorNationId: general.nationId,
generals,
nationNames: new Map(nations.map((entry) => [entry.id, entry.name])),
cityNames: new Map(cities.map((entry) => [entry.id, entry.name])),
});
const items: TurnCommandInputOptions['items'] = {
horse: [{ value: 'None', label: '판매/해제' }],
weapon: [{ value: 'None', label: '판매/해제' }],
@@ -226,12 +239,8 @@ export const getTurnCommandTable = async (ctx: GameApiContext, generalId: number
label: entry.name,
color: entry.color,
})),
generals: generals.map((entry) => ({
value: entry.id,
label: `${entry.name} (${nationById.get(entry.nationId)?.name ?? '무소속'} · ${
cityById.get(entry.cityId)?.name ?? '재야'
})`,
})),
generals: generalTargetOptions.generals,
generalTargets: generalTargetOptions.generalTargets,
crewTypes: (environment.unitSet.crewTypes ?? [])
.filter((entry) => !entry.requirements.some((requirement) => requirement.type === 'Impossible'))
.map((entry) => ({ value: entry.id, label: entry.name })),
@@ -446,7 +455,7 @@ export const turnsRouter = router({
await assertReservedTurnPermission(worldState, general, 'nation', input.action, args);
const snapshot = await mutateReservedTurns(() =>
setNationTurn(
setNationTurnAtCurrentPosition(
ctx.db,
general.nationId,
general.officerLevel,
@@ -559,7 +568,13 @@ export const turnsRouter = router({
await assertReservedTurnPermission(worldState, general, 'nation', update.action, update.args);
}
const snapshot = await mutateReservedTurns(() =>
setNationTurns(ctx.db, general.nationId, general.officerLevel, updates, input.expectedRevision)
setNationTurnsAtCurrentPositions(
ctx.db,
general.nationId,
general.officerLevel,
updates,
input.expectedRevision
)
);
return { ok: true, ...snapshot };
}),