Files
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

29 lines
2.2 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { createItemNameSession } from '../src/gui/itemNames';
function deferred<T>() { let resolve!: (value: T) => void; const promise=new Promise<T>(r=>{resolve=r;});return{resolve,promise}; }
describe('礼包关联物品名称',()=>{
it('合并普通与自选奖励 ID,只查询一次重复 ID,保留未知物品标记',async()=>{
const read=vi.fn(async()=>({'1':'药剂','4':'装备😀'}));const session=createItemNameSession(read);
await session.load(['1','4','1',' 1 ','999','','invalid']);
expect(read).toHaveBeenCalledExactlyOnceWith(['1','4','999']);expect(session.names.value).toEqual({'1':'药剂','4':'装备😀','999':''});
});
it('修改奖励 ID 后淘汰旧名称,延迟结果不能覆盖新奖励',async()=>{
const old=deferred<Record<string,string>>(),next=deferred<Record<string,string>>();let calls=0;
const session=createItemNameSession(()=>++calls===1?old.promise:next.promise);
const first=session.load(['1']);const second=session.load(['2']);next.resolve({'2':'新奖励'});await second;
old.resolve({'1':'旧奖励'});await first;expect(session.names.value).toEqual({'2':'新奖励'});
});
it('切换归档清空名称并忽略旧归档响应,防止同 ID 串名',async()=>{
const pending=deferred<Record<string,string>>();const session=createItemNameSession(()=>pending.promise);
const first=session.load(['1']);session.invalidate(true);pending.resolve({'1':'旧归档'});await first;
expect(session.names.value).toEqual({});expect(session.loading.value).toBe(false);
});
it('同归档刷新保留已显示名称,失败后可以重试,空列表不发请求',async()=>{
const read=vi.fn().mockResolvedValueOnce({'1':'原名称'}).mockRejectedValueOnce(new Error('read failed')).mockResolvedValueOnce({'1':'更新名称'});
const session=createItemNameSession(read);await session.load(['1']);session.invalidate();await session.load(['1']);
expect(session.names.value['1']).toBe('原名称');expect(session.error.value).toContain('read failed');
await session.load(['1']);expect(session.names.value['1']).toBe('更新名称');expect(session.error.value).toBe('');
await session.load([]);expect(session.names.value).toEqual({});expect(read).toHaveBeenCalledTimes(3);
});
});