merge: 최신 main을 외교 권한 복수 임명 수정에 다시 통합한다
This commit is contained in:
@@ -695,7 +695,13 @@ export const getGeneralContext = async (ctx: GameApiContext) => {
|
||||
export const generalRouter = router({
|
||||
adjustIcon: engineAuthedProcedure
|
||||
.input(
|
||||
z.object({ iconId: z.string().uuid().optional(), clientRequestId: z.string().uuid().optional() }).optional()
|
||||
z
|
||||
.object({
|
||||
iconId: z.string().uuid().optional(),
|
||||
resetToDefault: z.literal(true).optional(),
|
||||
clientRequestId: z.string().uuid().optional(),
|
||||
})
|
||||
.optional()
|
||||
)
|
||||
.mutation(({ ctx, input }) => {
|
||||
const userId = ctx.auth?.user.id;
|
||||
@@ -703,19 +709,41 @@ export const generalRouter = router({
|
||||
throw new TRPCError({ code: 'UNAUTHORIZED' });
|
||||
}
|
||||
const selected = input?.iconId ? ctx.auth?.user.icons?.find((icon) => icon.id === input.iconId) : undefined;
|
||||
if (input?.iconId && (!selected || ctx.auth?.user.canUseGeneralPicture === false)) {
|
||||
const resetToDefault = input?.resetToDefault === true;
|
||||
if (resetToDefault && input?.iconId) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: '아이콘 선택과 기본 아이콘 초기화를 함께 요청할 수 없습니다.' });
|
||||
}
|
||||
if (!resetToDefault && !input?.iconId) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: '적용할 활성 전용 아이콘을 선택해 주세요.' });
|
||||
}
|
||||
if (
|
||||
resetToDefault &&
|
||||
(ctx.auth?.user.picture !== 'default.jpg' || ctx.auth?.user.imageServer !== 0)
|
||||
) {
|
||||
throw new TRPCError({ code: 'FORBIDDEN', message: '현재 계정 아이콘이 기본 아이콘이 아닙니다.' });
|
||||
}
|
||||
if (!resetToDefault && (!selected || ctx.auth?.user.canUseGeneralPicture === false)) {
|
||||
throw new TRPCError({ code: 'FORBIDDEN', message: '사용 가능한 내 전용 아이콘이 아닙니다.' });
|
||||
}
|
||||
const iconRevision = ctx.auth?.user.iconUpdatedAt ?? (resetToDefault ? undefined : selected!.createdAt);
|
||||
if (!iconRevision) {
|
||||
throw new TRPCError({ code: 'PRECONDITION_FAILED', message: '계정 아이콘 변경 시각을 확인할 수 없습니다.' });
|
||||
}
|
||||
const projection = resetToDefault
|
||||
? {
|
||||
picture: 'default.jpg',
|
||||
imageServer: 0,
|
||||
revision: iconRevision,
|
||||
}
|
||||
: {
|
||||
picture: selected!.picture,
|
||||
imageServer: selected!.imageServer,
|
||||
revision: iconRevision,
|
||||
};
|
||||
return adjustAccountIconForUser(
|
||||
ctx,
|
||||
userId,
|
||||
selected
|
||||
? {
|
||||
picture: selected.picture,
|
||||
imageServer: selected.imageServer,
|
||||
revision: ctx.auth?.user.iconUpdatedAt ?? selected.createdAt,
|
||||
}
|
||||
: undefined,
|
||||
projection,
|
||||
true,
|
||||
input?.clientRequestId ?? ctx.requestId
|
||||
);
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
WAR_TRAIT_KEYS,
|
||||
} from '@sammo-ts/logic';
|
||||
import { readInheritancePoint, resolveInheritConstants } from '../../services/inheritance.js';
|
||||
import { loadAuthoritativeAccountIcon } from '../../services/accountIconSync.js';
|
||||
import { loadCurrentGameTime } from '../../services/gameClock.js';
|
||||
import { getSelectionPoolStatus, resolveSelectionMaxGeneral } from '@sammo-ts/game-engine/turn/selectPoolService.js';
|
||||
import {
|
||||
@@ -515,15 +514,17 @@ export const joinRouter = router({
|
||||
if (input.iconId && (!selectedIcon || auth.user.canUseGeneralPicture === false)) {
|
||||
throw new TRPCError({ code: 'FORBIDDEN', message: '사용 가능한 내 전용 아이콘이 아닙니다.' });
|
||||
}
|
||||
const accountIcon = input.pic
|
||||
? selectedIcon
|
||||
// 유저 장수에는 인증 token의 활성 전용 아이콘을 명시적으로 고른 경우만
|
||||
// 그림을 적용한다. Gateway 대표 그림은 shared preset일 수 있으므로
|
||||
// iconId 없는 fallback으로 사용하지 않는다.
|
||||
const accountIcon =
|
||||
input.pic && selectedIcon
|
||||
? {
|
||||
picture: selectedIcon.picture,
|
||||
imageServer: selectedIcon.imageServer,
|
||||
revision: auth.user.iconUpdatedAt ?? selectedIcon.createdAt,
|
||||
}
|
||||
: await loadAuthoritativeAccountIcon(ctx, userId)
|
||||
: null;
|
||||
: null;
|
||||
const commandRequestId = resolveJoinCreateRequestId(ctx.requestId, userId, input.clientRequestId);
|
||||
const result = await requestJoinCreateCommand(ctx, {
|
||||
type: 'joinCreateGeneral',
|
||||
@@ -535,7 +536,7 @@ export const joinRouter = router({
|
||||
leadership: input.leadership,
|
||||
strength: input.strength,
|
||||
intel: input.intel,
|
||||
pic: input.pic,
|
||||
pic: accountIcon !== null,
|
||||
character: input.character,
|
||||
profileId: ctx.profile.id,
|
||||
...(accountIcon
|
||||
|
||||
@@ -40,7 +40,7 @@ export const loadAuthoritativeAccountIcon = async (
|
||||
export const adjustAccountIconForUser = async (
|
||||
ctx: GameApiContext,
|
||||
userId: string,
|
||||
selected?: AccountIconProjection,
|
||||
selected: AccountIconProjection,
|
||||
enforceCooldown = true,
|
||||
requestKey?: string
|
||||
): Promise<{
|
||||
@@ -48,10 +48,8 @@ export const adjustAccountIconForUser = async (
|
||||
generalId: number | null;
|
||||
updated: boolean;
|
||||
}> => {
|
||||
const projection = selected ?? (await loadAuthoritativeAccountIcon(ctx, userId));
|
||||
const requestId = selected
|
||||
? `general:adjustIcon:${userId}:manual:${requestKey ?? `${projection.revision}:${encodeURIComponent(projection.picture)}`}`
|
||||
: `general:adjustIcon:${userId}:${projection.revision}`;
|
||||
const projection = selected;
|
||||
const requestId = `general:adjustIcon:${userId}:manual:${requestKey ?? `${projection.revision}:${encodeURIComponent(projection.picture)}`}`;
|
||||
try {
|
||||
const result = await ctx.turnDaemon.requestCommand({
|
||||
type: 'adjustGeneralIcon',
|
||||
|
||||
Reference in New Issue
Block a user