feat: 장수 관련 명령어 처리 및 설정 기능 추가
This commit is contained in:
@@ -22,6 +22,7 @@ export interface WorldStateRow {
|
||||
|
||||
export interface GeneralRow {
|
||||
id: number;
|
||||
userId: number | null;
|
||||
name: string;
|
||||
nationId: number;
|
||||
cityId: number;
|
||||
|
||||
@@ -34,6 +34,13 @@ import { loadWorldMap } from './maps/worldMap.js';
|
||||
const zRunReason = z.enum(['schedule', 'manual', 'poke']);
|
||||
const zMessageType = z.enum(['private', 'public', 'national', 'diplomacy']);
|
||||
|
||||
const zGeneralSettings = z.object({
|
||||
tnmt: z.number().int().optional(),
|
||||
defence_train: z.number().int().optional(),
|
||||
use_treatment: z.number().int().optional(),
|
||||
use_auto_nation_turn: z.number().int().optional(),
|
||||
});
|
||||
|
||||
const zTurnRunBudget = z.object({
|
||||
budgetMs: z.number().int().positive(),
|
||||
maxGenerals: z.number().int().positive(),
|
||||
@@ -60,6 +67,19 @@ const toWorldStateSnapshot = (row: WorldStateRow) => ({
|
||||
updatedAt: row.updatedAt.toISOString(),
|
||||
});
|
||||
|
||||
const getMyGeneral = async (ctx: { db: any, auth: any }) => {
|
||||
if (!ctx.auth?.user.id) {
|
||||
throw new TRPCError({ code: 'UNAUTHORIZED' });
|
||||
}
|
||||
const general = await ctx.db.general.findFirst({
|
||||
where: { userId: parseInt(ctx.auth.user.id) },
|
||||
});
|
||||
if (!general) {
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'General not found' });
|
||||
}
|
||||
return general;
|
||||
};
|
||||
|
||||
export const appRouter = router({
|
||||
health: router({
|
||||
ping: procedure.query(({ ctx }) => ({
|
||||
@@ -693,6 +713,161 @@ export const appRouter = router({
|
||||
return { ok: true, wasLeader: result.wasLeader };
|
||||
}),
|
||||
}),
|
||||
general: router({
|
||||
dieOnPrestart: authedProcedure.mutation(async ({ ctx }) => {
|
||||
const general = await getMyGeneral(ctx);
|
||||
const result = await ctx.turnDaemon.requestCommand({
|
||||
type: 'dieOnPrestart',
|
||||
generalId: general.id,
|
||||
});
|
||||
if (!result || result.type !== 'dieOnPrestart') {
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unexpected response' });
|
||||
}
|
||||
if (!result.ok) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: result.reason });
|
||||
}
|
||||
return { ok: true };
|
||||
}),
|
||||
buildNationCandidate: authedProcedure.mutation(async ({ ctx }) => {
|
||||
const general = await getMyGeneral(ctx);
|
||||
const result = await ctx.turnDaemon.requestCommand({
|
||||
type: 'buildNationCandidate',
|
||||
generalId: general.id,
|
||||
});
|
||||
if (!result || result.type !== 'buildNationCandidate') {
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unexpected response' });
|
||||
}
|
||||
if (!result.ok) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: result.reason });
|
||||
}
|
||||
return { ok: true };
|
||||
}),
|
||||
instantRetreat: authedProcedure.mutation(async ({ ctx }) => {
|
||||
const general = await getMyGeneral(ctx);
|
||||
const result = await ctx.turnDaemon.requestCommand({
|
||||
type: 'instantRetreat',
|
||||
generalId: general.id,
|
||||
});
|
||||
if (!result || result.type !== 'instantRetreat') {
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unexpected response' });
|
||||
}
|
||||
if (!result.ok) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: result.reason });
|
||||
}
|
||||
return { ok: true };
|
||||
}),
|
||||
vacation: authedProcedure.mutation(async ({ ctx }) => {
|
||||
const general = await getMyGeneral(ctx);
|
||||
const result = await ctx.turnDaemon.requestCommand({
|
||||
type: 'vacation',
|
||||
generalId: general.id,
|
||||
});
|
||||
if (!result || result.type !== 'vacation') {
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unexpected response' });
|
||||
}
|
||||
if (!result.ok) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: result.reason });
|
||||
}
|
||||
return { ok: true };
|
||||
}),
|
||||
setMySetting: authedProcedure
|
||||
.input(zGeneralSettings)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const general = await getMyGeneral(ctx);
|
||||
const result = await ctx.turnDaemon.requestCommand({
|
||||
type: 'setMySetting',
|
||||
generalId: general.id,
|
||||
settings: input,
|
||||
});
|
||||
if (!result || result.type !== 'setMySetting') {
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unexpected response' });
|
||||
}
|
||||
if (!result.ok) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: result.reason });
|
||||
}
|
||||
return { ok: true };
|
||||
}),
|
||||
dropItem: authedProcedure
|
||||
.input(z.object({ itemType: z.string() }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const general = await getMyGeneral(ctx);
|
||||
const result = await ctx.turnDaemon.requestCommand({
|
||||
type: 'dropItem',
|
||||
generalId: general.id,
|
||||
itemType: input.itemType,
|
||||
});
|
||||
if (!result || result.type !== 'dropItem') {
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unexpected response' });
|
||||
}
|
||||
if (!result.ok) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: result.reason });
|
||||
}
|
||||
return { ok: true };
|
||||
}),
|
||||
}),
|
||||
nation: router({
|
||||
changePermission: authedProcedure
|
||||
.input(z.object({
|
||||
isAmbassador: z.boolean(),
|
||||
targetGeneralIds: z.array(z.number().int().positive()),
|
||||
}))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const general = await getMyGeneral(ctx);
|
||||
const result = await ctx.turnDaemon.requestCommand({
|
||||
type: 'changePermission',
|
||||
generalId: general.id,
|
||||
isAmbassador: input.isAmbassador,
|
||||
targetGeneralIds: input.targetGeneralIds,
|
||||
});
|
||||
if (!result || result.type !== 'changePermission') {
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unexpected response' });
|
||||
}
|
||||
if (!result.ok) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: result.reason });
|
||||
}
|
||||
return { ok: true };
|
||||
}),
|
||||
kick: authedProcedure
|
||||
.input(z.object({ destGeneralId: z.number().int().positive() }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const general = await getMyGeneral(ctx);
|
||||
const result = await ctx.turnDaemon.requestCommand({
|
||||
type: 'kick',
|
||||
generalId: general.id,
|
||||
destGeneralId: input.destGeneralId,
|
||||
});
|
||||
if (!result || result.type !== 'kick') {
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unexpected response' });
|
||||
}
|
||||
if (!result.ok) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: result.reason });
|
||||
}
|
||||
return { ok: true };
|
||||
}),
|
||||
appoint: authedProcedure
|
||||
.input(z.object({
|
||||
destGeneralId: z.number().int().nonnegative(),
|
||||
destCityId: z.number().int().nonnegative(),
|
||||
officerLevel: z.number().int().nonnegative(),
|
||||
}))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const general = await getMyGeneral(ctx);
|
||||
const result = await ctx.turnDaemon.requestCommand({
|
||||
type: 'appoint',
|
||||
generalId: general.id,
|
||||
destGeneralId: input.destGeneralId,
|
||||
destCityId: input.destCityId,
|
||||
officerLevel: input.officerLevel,
|
||||
});
|
||||
if (!result || result.type !== 'appoint') {
|
||||
throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Unexpected response' });
|
||||
}
|
||||
if (!result.ok) {
|
||||
throw new TRPCError({ code: 'BAD_REQUEST', message: result.reason });
|
||||
}
|
||||
return { ok: true };
|
||||
}),
|
||||
}),
|
||||
turnDaemon: router({
|
||||
run: procedure
|
||||
.input(
|
||||
|
||||
Reference in New Issue
Block a user