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

243 lines
7.5 KiB
Go

package services
import (
"testing"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/events"
)
type fakeScriptWindow struct {
focusCount int
closeCount int
// hook 保存注册进来的 WindowClosing 钩子,便于在测试里模拟关窗。
hook func(event *application.WindowEvent)
}
func (w *fakeScriptWindow) Focus() { w.focusCount++ }
func (w *fakeScriptWindow) Close() { w.closeCount++ }
func (w *fakeScriptWindow) RegisterHook(
eventType events.WindowEventType,
callback func(event *application.WindowEvent),
) func() {
if eventType == events.Common.WindowClosing {
w.hook = callback
}
return func() { w.hook = nil }
}
type fakeScriptWindowHost struct {
windows map[string]*fakeScriptWindow
createCount int
lastOptions application.WebviewWindowOptions
}
func newFakeScriptWindowHost() *fakeScriptWindowHost {
return &fakeScriptWindowHost{windows: map[string]*fakeScriptWindow{}}
}
func (h *fakeScriptWindowHost) findWindow(name string) (scriptWindowHandle, bool) {
window, ok := h.windows[name]
if !ok {
return nil, false
}
return window, true
}
func (h *fakeScriptWindowHost) createWindow(
options application.WebviewWindowOptions,
) (scriptWindowHandle, error) {
h.createCount++
h.lastOptions = options
window := &fakeScriptWindow{}
h.windows[options.Name] = window
return window, nil
}
func TestOpenScriptWindowCreatesWindowOnce(t *testing.T) {
host := newFakeScriptWindowHost()
service := newScriptWindowService(host)
session := ScriptSession{Name: "a.pvf.js", Source: "src", SavedSource: "saved"}
if err := service.OpenScriptWindow(session); err != nil {
t.Fatalf("OpenScriptWindow() error = %v", err)
}
if host.createCount != 1 {
t.Fatalf("createCount = %d, want 1", host.createCount)
}
// 第二次调用必须聚焦已有窗口,而不是再开一个。
if err := service.OpenScriptWindow(session); err != nil {
t.Fatalf("second OpenScriptWindow() error = %v", err)
}
if host.createCount != 1 {
t.Errorf("createCount = %d after second call, want 1", host.createCount)
}
window := host.windows[ScriptWindowName]
if window.focusCount != 1 {
t.Errorf("focusCount = %d, want 1", window.focusCount)
}
}
func TestOpenScriptWindowUsesQueryViewAndName(t *testing.T) {
host := newFakeScriptWindowHost()
service := newScriptWindowService(host)
if err := service.OpenScriptWindow(ScriptSession{}); err != nil {
t.Fatalf("OpenScriptWindow() error = %v", err)
}
// 名称必须显式设置,否则 Wails 会用 window-N,按名字查不到窗口。
if host.lastOptions.Name != ScriptWindowName {
t.Errorf("Name = %q, want %q", host.lastOptions.Name, ScriptWindowName)
}
// 资产服务器没有 SPA 回退,视图只能通过 query 选择。
if host.lastOptions.URL != "/?view=script" {
t.Errorf("URL = %q, want %q", host.lastOptions.URL, "/?view=script")
}
}
func TestScriptSessionRoundTrip(t *testing.T) {
host := newFakeScriptWindowHost()
service := newScriptWindowService(host)
if got := service.LoadScriptSession(); got != nil {
t.Fatalf("LoadScriptSession() = %v before staging, want nil", got)
}
want := ScriptSession{Name: "b.pvf.js", Source: "new", SavedSource: "old"}
if err := service.OpenScriptWindow(want); err != nil {
t.Fatalf("OpenScriptWindow() error = %v", err)
}
got := service.LoadScriptSession()
if got == nil {
t.Fatal("LoadScriptSession() = nil, want session")
}
if *got != want {
t.Errorf("LoadScriptSession() = %+v, want %+v", *got, want)
}
// 返回的是副本:调用方改动不应影响服务内部状态。
got.Source = "mutated"
if again := service.LoadScriptSession(); again == nil || again.Source != "new" {
t.Errorf("stored session was mutated by caller: %+v", again)
}
}
func TestScriptWindowCloseHookBlocksUntilAllowed(t *testing.T) {
host := newFakeScriptWindowHost()
service := newScriptWindowService(host)
if err := service.OpenScriptWindow(ScriptSession{}); err != nil {
t.Fatalf("OpenScriptWindow() error = %v", err)
}
window := host.windows[ScriptWindowName]
if window.hook == nil {
t.Fatal("WindowClosing hook was not registered")
}
// 用户主动关窗:钩子应拦下原生关闭,交给前端确认。
blocked := application.NewWindowEvent()
window.hook(blocked)
if !blocked.IsCancelled() {
t.Error("hook did not cancel the close, want cancelled for confirmation")
}
// 前端确认后调用关闭:钩子应放行。
if err := service.CloseScriptWindow(ScriptSession{Name: "c.pvf.js"}); err != nil {
t.Fatalf("CloseScriptWindow() error = %v", err)
}
if window.closeCount != 1 {
t.Errorf("closeCount = %d, want 1", window.closeCount)
}
allowed := application.NewWindowEvent()
window.hook(allowed)
if allowed.IsCancelled() {
t.Error("hook cancelled an approved close, want allowed")
}
// 放行标志是一次性的:下一个关闭请求必须重新走确认。
again := application.NewWindowEvent()
window.hook(again)
if !again.IsCancelled() {
t.Error("close approval leaked into the next close request")
}
}
func TestCloseScriptWindowWithoutWindowIsSafe(t *testing.T) {
service := newScriptWindowService(newFakeScriptWindowHost())
// 窗口未打开时关闭不应 panic,且仍应记住交回的会话。
if err := service.CloseScriptWindow(ScriptSession{Name: "d.pvf.js", Source: "s"}); err != nil {
t.Fatalf("CloseScriptWindow() error = %v", err)
}
if got := service.LoadScriptSession(); got == nil || got.Name != "d.pvf.js" {
t.Errorf("LoadScriptSession() = %+v, want staged session", got)
}
service.CloseScriptWindowIfOpen()
}
func TestFocusScriptWindowReportsExistence(t *testing.T) {
host := newFakeScriptWindowHost()
service := newScriptWindowService(host)
if service.FocusScriptWindow() {
t.Error("FocusScriptWindow() = true with no window, want false")
}
if service.IsScriptWindowOpen() {
t.Error("IsScriptWindowOpen() = true with no window, want false")
}
if err := service.OpenScriptWindow(ScriptSession{}); err != nil {
t.Fatalf("OpenScriptWindow() error = %v", err)
}
if !service.IsScriptWindowOpen() {
t.Error("IsScriptWindowOpen() = false after open, want true")
}
if !service.FocusScriptWindow() {
t.Error("FocusScriptWindow() = false after open, want true")
}
if host.windows[ScriptWindowName].focusCount != 1 {
t.Errorf("focusCount = %d, want 1", host.windows[ScriptWindowName].focusCount)
}
}
// 应用退出流程必须能放行脚本窗口,否则退出会卡在它的关闭确认上。
func TestAllowScriptWindowCloseReleasesHook(t *testing.T) {
host := newFakeScriptWindowHost()
service := newScriptWindowService(host)
if err := service.OpenScriptWindow(ScriptSession{}); err != nil {
t.Fatalf("OpenScriptWindow() error = %v", err)
}
window := host.windows[ScriptWindowName]
service.AllowScriptWindowClose()
service.CloseScriptWindowIfOpen()
if window.closeCount != 1 {
t.Fatalf("closeCount = %d, want 1", window.closeCount)
}
event := application.NewWindowEvent()
window.hook(event)
if event.IsCancelled() {
t.Error("hook cancelled the quit-path close, want allowed")
}
}
func TestScriptWindowServiceWithoutHostIsSafe(t *testing.T) {
service := newScriptWindowService(nil)
if err := service.OpenScriptWindow(ScriptSession{}); err == nil {
t.Error("OpenScriptWindow() with nil host = nil error, want error")
}
// 其余方法在无窗口环境下不应 panic。
service.AllowScriptWindowClose()
service.CloseScriptWindowIfOpen()
if service.FocusScriptWindow() {
t.Error("FocusScriptWindow() = true with nil host, want false")
}
if got := service.LoadScriptSession(); got != nil {
t.Errorf("LoadScriptSession() = %+v, want nil", got)
}
}