fix: 자동 외교 설정을 제거하고 관련된 모든 부분을 업데이트
This commit is contained in:
@@ -76,12 +76,11 @@ const isFrontStatusPollActive = (poll: FrontStatusPoll, gameTime: CurrentGameTim
|
|||||||
return started && !ended;
|
return started && !ended;
|
||||||
};
|
};
|
||||||
|
|
||||||
const zGeneralSettings = z.object({
|
const zGeneralSettings = z.strictObject({
|
||||||
tnmt: z.number().int().optional(),
|
tnmt: z.number().int().optional(),
|
||||||
defence_train: z.number().int().optional(),
|
defence_train: z.number().int().optional(),
|
||||||
use_treatment: z.number().int().optional(),
|
use_treatment: z.number().int().optional(),
|
||||||
use_auto_nation_turn: z.number().int().optional(),
|
use_auto_nation_turn: z.number().int().optional(),
|
||||||
use_auto_nation_diplomacy: z.number().int().min(0).max(1).optional(),
|
|
||||||
use_auto_nation_war: z.number().int().min(0).max(1).optional(),
|
use_auto_nation_war: z.number().int().min(0).max(1).optional(),
|
||||||
use_auto_nation_promotion: z.number().int().min(0).max(1).optional(),
|
use_auto_nation_promotion: z.number().int().min(0).max(1).optional(),
|
||||||
use_auto_nation_finance: z.number().int().min(0).max(1).optional(),
|
use_auto_nation_finance: z.number().int().min(0).max(1).optional(),
|
||||||
@@ -244,7 +243,6 @@ const resolveUserSettings = (meta: Record<string, unknown>) => {
|
|||||||
use_auto_nation_turn: readNumber(readSetting('use_auto_nation_turn'), 1),
|
use_auto_nation_turn: readNumber(readSetting('use_auto_nation_turn'), 1),
|
||||||
// Ref가 NPC 군주에게만 수행하던 국가 운영은 사용자 군주에게 opt-in이다.
|
// Ref가 NPC 군주에게만 수행하던 국가 운영은 사용자 군주에게 opt-in이다.
|
||||||
// 누락된 값은 신규 게임과 기존 장수 모두 안전한 기본값(사용 안함)으로 해석한다.
|
// 누락된 값은 신규 게임과 기존 장수 모두 안전한 기본값(사용 안함)으로 해석한다.
|
||||||
use_auto_nation_diplomacy: readNumber(readSetting('use_auto_nation_diplomacy'), 0),
|
|
||||||
use_auto_nation_war: readNumber(readSetting('use_auto_nation_war'), 0),
|
use_auto_nation_war: readNumber(readSetting('use_auto_nation_war'), 0),
|
||||||
use_auto_nation_promotion: readNumber(readSetting('use_auto_nation_promotion'), 0),
|
use_auto_nation_promotion: readNumber(readSetting('use_auto_nation_promotion'), 0),
|
||||||
use_auto_nation_finance: readNumber(readSetting('use_auto_nation_finance'), 0),
|
use_auto_nation_finance: readNumber(readSetting('use_auto_nation_finance'), 0),
|
||||||
|
|||||||
@@ -584,7 +584,6 @@ describe('in-game my information ownership', () => {
|
|||||||
defence_train: 80,
|
defence_train: 80,
|
||||||
use_treatment: 21,
|
use_treatment: 21,
|
||||||
use_auto_nation_turn: 1,
|
use_auto_nation_turn: 1,
|
||||||
use_auto_nation_diplomacy: 0,
|
|
||||||
use_auto_nation_war: 0,
|
use_auto_nation_war: 0,
|
||||||
use_auto_nation_promotion: 0,
|
use_auto_nation_promotion: 0,
|
||||||
use_auto_nation_finance: 0,
|
use_auto_nation_finance: 0,
|
||||||
@@ -612,6 +611,18 @@ describe('in-game my information ownership', () => {
|
|||||||
expect(requestCommand).not.toHaveBeenCalled();
|
expect(requestCommand).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('rejects the removed automatic diplomacy setting before dispatching it to ENGINE', async () => {
|
||||||
|
const requestCommand = vi.fn(async () => ({ type: 'setMySetting', ok: true, generalId: 7 }));
|
||||||
|
const fixture = createContext({ requestCommand });
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
appRouter.createCaller(fixture.context).general.setMySetting({
|
||||||
|
use_auto_nation_diplomacy: 1,
|
||||||
|
} as never)
|
||||||
|
).rejects.toMatchObject({ code: 'BAD_REQUEST' });
|
||||||
|
expect(requestCommand).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('sends settings directly to ENGINE without creating an API input event', async () => {
|
it('sends settings directly to ENGINE without creating an API input event', async () => {
|
||||||
const transaction = vi.fn(async () => {
|
const transaction = vi.fn(async () => {
|
||||||
throw new Error('API transaction must not run');
|
throw new Error('API transaction must not run');
|
||||||
|
|||||||
@@ -61,10 +61,9 @@ export const AVAILABLE_INSTANT_TURN: Record<string, boolean> = {
|
|||||||
NPC전방발령: true,
|
NPC전방발령: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserRulerAutomationFeature = 'diplomacy' | 'war' | 'promotion' | 'finance' | 'capital';
|
export type UserRulerAutomationFeature = 'war' | 'promotion' | 'finance' | 'capital';
|
||||||
|
|
||||||
const USER_RULER_AUTOMATION_META_KEY = {
|
const USER_RULER_AUTOMATION_META_KEY = {
|
||||||
diplomacy: 'use_auto_nation_diplomacy',
|
|
||||||
war: 'use_auto_nation_war',
|
war: 'use_auto_nation_war',
|
||||||
promotion: 'use_auto_nation_promotion',
|
promotion: 'use_auto_nation_promotion',
|
||||||
finance: 'use_auto_nation_finance',
|
finance: 'use_auto_nation_finance',
|
||||||
@@ -72,7 +71,6 @@ const USER_RULER_AUTOMATION_META_KEY = {
|
|||||||
} as const satisfies Record<UserRulerAutomationFeature, string>;
|
} as const satisfies Record<UserRulerAutomationFeature, string>;
|
||||||
|
|
||||||
const USER_RULER_ACTION_FEATURE: Readonly<Record<string, UserRulerAutomationFeature>> = {
|
const USER_RULER_ACTION_FEATURE: Readonly<Record<string, UserRulerAutomationFeature>> = {
|
||||||
불가침제의: 'diplomacy',
|
|
||||||
선전포고: 'war',
|
선전포고: 'war',
|
||||||
천도: 'capital',
|
천도: 'capital',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -142,12 +142,11 @@ const zSetMySetting = z.object({
|
|||||||
type: z.literal('setMySetting'),
|
type: z.literal('setMySetting'),
|
||||||
userId: z.string().min(1),
|
userId: z.string().min(1),
|
||||||
generalId: zFiniteNumber,
|
generalId: zFiniteNumber,
|
||||||
settings: z.object({
|
settings: z.strictObject({
|
||||||
tnmt: z.number().int().optional(),
|
tnmt: z.number().int().optional(),
|
||||||
defence_train: z.number().int().optional(),
|
defence_train: z.number().int().optional(),
|
||||||
use_treatment: z.number().int().optional(),
|
use_treatment: z.number().int().optional(),
|
||||||
use_auto_nation_turn: z.number().int().optional(),
|
use_auto_nation_turn: z.number().int().optional(),
|
||||||
use_auto_nation_diplomacy: z.number().int().optional(),
|
|
||||||
use_auto_nation_war: z.number().int().optional(),
|
use_auto_nation_war: z.number().int().optional(),
|
||||||
use_auto_nation_promotion: z.number().int().optional(),
|
use_auto_nation_promotion: z.number().int().optional(),
|
||||||
use_auto_nation_finance: z.number().int().optional(),
|
use_auto_nation_finance: z.number().int().optional(),
|
||||||
|
|||||||
@@ -1896,7 +1896,6 @@ async function handleSetMySetting(
|
|||||||
nextMeta.use_auto_nation_turn = settings.use_auto_nation_turn === 0 ? 0 : 1;
|
nextMeta.use_auto_nation_turn = settings.use_auto_nation_turn === 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
for (const key of [
|
for (const key of [
|
||||||
'use_auto_nation_diplomacy',
|
|
||||||
'use_auto_nation_war',
|
'use_auto_nation_war',
|
||||||
'use_auto_nation_promotion',
|
'use_auto_nation_promotion',
|
||||||
'use_auto_nation_finance',
|
'use_auto_nation_finance',
|
||||||
|
|||||||
@@ -98,6 +98,23 @@ describe('authenticated actor-bound command registry and execution', () => {
|
|||||||
).toBeNull();
|
).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('rejects the removed automatic diplomacy setting at the daemon boundary', () => {
|
||||||
|
expect(
|
||||||
|
normalizeTurnDaemonCommand({
|
||||||
|
requestId: 'set-my-setting:removed-diplomacy',
|
||||||
|
sentAt: '2026-08-28T00:00:00.000Z',
|
||||||
|
command: {
|
||||||
|
type: 'setMySetting',
|
||||||
|
userId: 'user-7',
|
||||||
|
generalId: 7,
|
||||||
|
settings: {
|
||||||
|
use_auto_nation_diplomacy: 1,
|
||||||
|
},
|
||||||
|
} as unknown as TurnDaemonCommand,
|
||||||
|
})
|
||||||
|
).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it('rejects every actor-bound queue payload that omits userId', () => {
|
it('rejects every actor-bound queue payload that omits userId', () => {
|
||||||
for (const command of buildActorBoundCommands()) {
|
for (const command of buildActorBoundCommands()) {
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -826,7 +826,7 @@ describe('legacy NPC user-chief promotion parity', () => {
|
|||||||
expect(run(3, 0, 1)).toEqual([{ generalId: 2, officerLevel: 11, officerCity: 0, permission: 'ambassador' }]);
|
expect(run(3, 0, 1)).toEqual([{ generalId: 2, officerLevel: 11, officerCity: 0, permission: 'ambassador' }]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('keeps user-ruler duties individually disabled until each setting is enabled', () => {
|
it('keeps non-aggression proposals NPC-only while user-ruler duties remain opt-in', () => {
|
||||||
const ruler = makePromotionGeneral({ id: 1, officerLevel: 12, npcState: 0, meta: { killturn: 0 } });
|
const ruler = makePromotionGeneral({ id: 1, officerLevel: 12, npcState: 0, meta: { killturn: 0 } });
|
||||||
expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(false);
|
expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(false);
|
||||||
expect(canUseAutomatedNationAction(ruler, '불가침제의')).toBe(false);
|
expect(canUseAutomatedNationAction(ruler, '불가침제의')).toBe(false);
|
||||||
@@ -839,18 +839,21 @@ describe('legacy NPC user-chief promotion parity', () => {
|
|||||||
use_auto_nation_capital: 1,
|
use_auto_nation_capital: 1,
|
||||||
use_auto_nation_finance: 1,
|
use_auto_nation_finance: 1,
|
||||||
};
|
};
|
||||||
expect(canUseAutomatedNationAction(ruler, '불가침제의')).toBe(true);
|
// 기존 DB에 제거된 플래그가 남아 있어도 사용자 군주에게는 다시 활성화되지 않는다.
|
||||||
|
expect(canUseAutomatedNationAction(ruler, '불가침제의')).toBe(false);
|
||||||
expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(false);
|
expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(false);
|
||||||
expect(canUseAutomatedNationAction(ruler, '천도')).toBe(true);
|
expect(canUseAutomatedNationAction(ruler, '천도')).toBe(true);
|
||||||
expect(canUseRulerAutomation(ruler, 'finance')).toBe(true);
|
expect(canUseRulerAutomation(ruler, 'finance')).toBe(true);
|
||||||
|
|
||||||
ruler.meta = {
|
ruler.meta = {
|
||||||
...ruler.meta,
|
...ruler.meta,
|
||||||
use_auto_nation_diplomacy: 0,
|
|
||||||
use_auto_nation_war: 1,
|
use_auto_nation_war: 1,
|
||||||
};
|
};
|
||||||
expect(canUseAutomatedNationAction(ruler, '불가침제의')).toBe(false);
|
expect(canUseAutomatedNationAction(ruler, '불가침제의')).toBe(false);
|
||||||
expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(true);
|
expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(true);
|
||||||
|
|
||||||
|
const npcRuler = makePromotionGeneral({ id: 2, officerLevel: 12, npcState: 2, meta: { killturn: 0 } });
|
||||||
|
expect(canUseAutomatedNationAction(npcRuler, '불가침제의')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('honors the existing automatic nation-turn master switch for user chiefs only', () => {
|
it('honors the existing automatic nation-turn master switch for user chiefs only', () => {
|
||||||
|
|||||||
@@ -237,7 +237,6 @@ describe('my information world commands', () => {
|
|||||||
defence_train: 94,
|
defence_train: 94,
|
||||||
use_treatment: 200,
|
use_treatment: 200,
|
||||||
use_auto_nation_turn: 0,
|
use_auto_nation_turn: 0,
|
||||||
use_auto_nation_diplomacy: 1,
|
|
||||||
use_auto_nation_war: 1,
|
use_auto_nation_war: 1,
|
||||||
use_auto_nation_promotion: 1,
|
use_auto_nation_promotion: 1,
|
||||||
use_auto_nation_finance: 1,
|
use_auto_nation_finance: 1,
|
||||||
@@ -254,7 +253,6 @@ describe('my information world commands', () => {
|
|||||||
defence_train: 999,
|
defence_train: 999,
|
||||||
use_treatment: 100,
|
use_treatment: 100,
|
||||||
use_auto_nation_turn: 0,
|
use_auto_nation_turn: 0,
|
||||||
use_auto_nation_diplomacy: 1,
|
|
||||||
use_auto_nation_war: 1,
|
use_auto_nation_war: 1,
|
||||||
use_auto_nation_promotion: 1,
|
use_auto_nation_promotion: 1,
|
||||||
use_auto_nation_finance: 1,
|
use_auto_nation_finance: 1,
|
||||||
|
|||||||
@@ -276,7 +276,6 @@ const myGeneral = (state: FixtureState) => ({
|
|||||||
defence_train: 80,
|
defence_train: 80,
|
||||||
use_treatment: 21,
|
use_treatment: 21,
|
||||||
use_auto_nation_turn: 1,
|
use_auto_nation_turn: 1,
|
||||||
use_auto_nation_diplomacy: 0,
|
|
||||||
use_auto_nation_war: 0,
|
use_auto_nation_war: 0,
|
||||||
use_auto_nation_promotion: 0,
|
use_auto_nation_promotion: 0,
|
||||||
use_auto_nation_finance: 0,
|
use_auto_nation_finance: 0,
|
||||||
@@ -1347,13 +1346,12 @@ test('내 정보&설정 keeps desktop density and becomes a 390px horizontal-ide
|
|||||||
]);
|
]);
|
||||||
const rulerAutomation = page.locator('.ruler-automation-settings');
|
const rulerAutomation = page.locator('.ruler-automation-settings');
|
||||||
await expect(rulerAutomation).toBeVisible();
|
await expect(rulerAutomation).toBeVisible();
|
||||||
const diplomacyAutomation = page.getByRole('checkbox', { name: '자동 외교 (불가침 제의)' });
|
await expect(page.getByRole('checkbox', { name: '자동 외교 (불가침 제의)' })).toHaveCount(0);
|
||||||
const warAutomation = page.getByRole('checkbox', { name: '자동 선전포고' });
|
const warAutomation = page.getByRole('checkbox', { name: '자동 선전포고' });
|
||||||
const promotionAutomation = page.getByRole('checkbox', { name: '자동 수뇌 임명' });
|
const promotionAutomation = page.getByRole('checkbox', { name: '자동 수뇌 임명' });
|
||||||
const financeAutomation = page.getByRole('checkbox', { name: '자동 세율·지급률 조정' });
|
const financeAutomation = page.getByRole('checkbox', { name: '자동 세율·지급률 조정' });
|
||||||
const capitalAutomation = page.getByRole('checkbox', { name: '자동 천도' });
|
const capitalAutomation = page.getByRole('checkbox', { name: '자동 천도' });
|
||||||
for (const checkbox of [
|
for (const checkbox of [
|
||||||
diplomacyAutomation,
|
|
||||||
warAutomation,
|
warAutomation,
|
||||||
promotionAutomation,
|
promotionAutomation,
|
||||||
financeAutomation,
|
financeAutomation,
|
||||||
@@ -1421,7 +1419,6 @@ test('내 정보&설정 keeps desktop density and becomes a 390px horizontal-ide
|
|||||||
await expect.poll(() => state.settingMutations.length).toBe(1);
|
await expect.poll(() => state.settingMutations.length).toBe(1);
|
||||||
expect(state.settingMutations[0]).not.toHaveProperty('generalId');
|
expect(state.settingMutations[0]).not.toHaveProperty('generalId');
|
||||||
expect(state.settingMutations[0]).toMatchObject({
|
expect(state.settingMutations[0]).toMatchObject({
|
||||||
use_auto_nation_diplomacy: 1,
|
|
||||||
use_auto_nation_war: 1,
|
use_auto_nation_war: 1,
|
||||||
use_auto_nation_promotion: 1,
|
use_auto_nation_promotion: 1,
|
||||||
use_auto_nation_finance: 1,
|
use_auto_nation_finance: 1,
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ type SettingForm = {
|
|||||||
defence_train: number;
|
defence_train: number;
|
||||||
use_treatment: number;
|
use_treatment: number;
|
||||||
use_auto_nation_turn: number;
|
use_auto_nation_turn: number;
|
||||||
use_auto_nation_diplomacy: number;
|
|
||||||
use_auto_nation_war: number;
|
use_auto_nation_war: number;
|
||||||
use_auto_nation_promotion: number;
|
use_auto_nation_promotion: number;
|
||||||
use_auto_nation_finance: number;
|
use_auto_nation_finance: number;
|
||||||
@@ -69,7 +68,6 @@ const form = reactive<SettingForm>({
|
|||||||
defence_train: 80,
|
defence_train: 80,
|
||||||
use_treatment: 10,
|
use_treatment: 10,
|
||||||
use_auto_nation_turn: 1,
|
use_auto_nation_turn: 1,
|
||||||
use_auto_nation_diplomacy: 0,
|
|
||||||
use_auto_nation_war: 0,
|
use_auto_nation_war: 0,
|
||||||
use_auto_nation_promotion: 0,
|
use_auto_nation_promotion: 0,
|
||||||
use_auto_nation_finance: 0,
|
use_auto_nation_finance: 0,
|
||||||
@@ -430,15 +428,6 @@ onMounted(() => {
|
|||||||
:disabled="form.use_auto_nation_turn === 0"
|
:disabled="form.use_auto_nation_turn === 0"
|
||||||
>
|
>
|
||||||
<legend>사용자 군주 자동 업무</legend>
|
<legend>사용자 군주 자동 업무</legend>
|
||||||
<label>
|
|
||||||
<input
|
|
||||||
v-model="form.use_auto_nation_diplomacy"
|
|
||||||
type="checkbox"
|
|
||||||
:true-value="1"
|
|
||||||
:false-value="0"
|
|
||||||
/>
|
|
||||||
자동 외교 (불가침 제의)
|
|
||||||
</label>
|
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
v-model="form.use_auto_nation_war"
|
v-model="form.use_auto_nation_war"
|
||||||
|
|||||||
@@ -172,7 +172,6 @@ export type TurnDaemonCommand =
|
|||||||
defence_train?: number;
|
defence_train?: number;
|
||||||
use_treatment?: number;
|
use_treatment?: number;
|
||||||
use_auto_nation_turn?: number;
|
use_auto_nation_turn?: number;
|
||||||
use_auto_nation_diplomacy?: number;
|
|
||||||
use_auto_nation_war?: number;
|
use_auto_nation_war?: number;
|
||||||
use_auto_nation_promotion?: number;
|
use_auto_nation_promotion?: number;
|
||||||
use_auto_nation_finance?: number;
|
use_auto_nation_finance?: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user