diff --git a/client-patch/90CN_web.ini b/client-patch/90CN_web.ini index a726a91..09e7023 100644 --- a/client-patch/90CN_web.ini +++ b/client-patch/90CN_web.ini @@ -4,14 +4,15 @@ enabled=1 ; 1 = 点小井盖时弹出我们自己的窗口(推荐,中文输入正常) ; 0 = 老行为:仍然用游戏内嵌的 IE,只把页面跳转到下面的 url external=1 -; 我们自己的窗口开着的时候,把游戏内嵌的面板藏起来;关掉窗口后自动放回去 +; 原面板全程压制:只要它露头就立刻藏起来,玩家永远看不到游戏自带的旧页面 suppress=1 ; 控制台默认置顶显示。本服客户端全屏=无边框窗口最大化,已实测置顶窗口能正常盖在 ; 游戏画面上,所以一般不用开。 ; 只有换成真·独占全屏(DirectX 独占、窗口压在画面下看不见)时才改成 1:开控制台前先把 ; 游戏最小化,关掉控制台再自动还原全屏。 minimize_game=0 -; 关掉我们自己的窗口时:0 = 把游戏面板放回去(安全),1 = 直接把面板关掉 +; 关掉我们自己的窗口时:1 = 直接关闭游戏原面板,并在之后 2.5 秒内继续压制它 +; (推荐,原面板不会残留、也不会再露面);0 = 把面板放回去(老行为,会看到旧 IE 页面) close_action=1 ; 我们自己的窗口尺寸 width=1040 diff --git a/client-patch/90CN_web_redirect.cpp b/client-patch/90CN_web_redirect.cpp index ab6b2ab..7a278df 100644 --- a/client-patch/90CN_web_redirect.cpp +++ b/client-patch/90CN_web_redirect.cpp @@ -631,24 +631,31 @@ DWORD WINAPI WorkerThread(void*) HWND imeHwnd = nullptr; unsigned imeTick = 0; - HWND hiddenPanel = nullptr; - HWND consumedPanel = nullptr; bool ownShown = false; int mode = g_cfg.external ? MODE_GM_WINDOW : MODE_INLINE; DWORD lastPoll = 0; DWORD lastLaunch = 0; + // 玩家关掉控制台之后的抑制期:这段时间里面板只要露头就继续藏、继续请它关闭, + // 免得游戏的关闭动画把旧页面又画出来(残留)。 + DWORD suppressUntil = 0; + int closeAttempts = 0; for (;;) { if (InterlockedCompareExchange(&g_stop, 0, 0) != 0) break; - // WebView2 要靠消息泵跑起来,窗口开着时必须泵得勤,否则页面会顿。 - GmPumpMessages(ownShown ? 50 : 250); - const DWORD now = GetTickCount(); - if (now - lastPoll < 250) continue; + const bool suppressing = suppressUntil && now < suppressUntil; + + // WebView2 要靠消息泵跑起来,窗口开着时必须泵得勤,否则页面会顿。 + GmPumpMessages((ownShown || suppressing) ? 40 : 120); + + // 原面板绝不能出现在画面上,所以轮询要快;空闲时放宽一点省 CPU。 + const DWORD interval = (ownShown || suppressing) ? 40 : 60; + if (now - lastPoll < interval) continue; lastPoll = now; const HWND panel = FindWebPanelWindow(); + const bool panelVisible = panel != nullptr && IsWindowVisible(panel) != FALSE; if (mode == MODE_INLINE) { HWND ieHwnd = FindIeServerWindow(); @@ -666,49 +673,59 @@ DWORD WINAPI WorkerThread(void*) continue; } - if (!panel) { - // 面板被游戏关掉了:把我们自己的窗口也收起来,状态归零 - if (mode == MODE_GM_WINDOW && GmWindowIsVisible()) GmWindowHide(); - RestoreGameFromConsole(); - hiddenPanel = nullptr; - consumedPanel = nullptr; - ownShown = false; - continue; - } - - const bool mine = OwnWindowVisible(mode); - - // 玩家把我们的窗口关了:把面板放回去(或直接关掉),交回游戏 - if (ownShown && !mine) { - ownShown = false; - HWND target = (hiddenPanel && IsWindow(hiddenPanel)) ? hiddenPanel : panel; - if (g_cfg.closePanel) { - PostMessageW(target, WM_CLOSE, 0, 0); - LogLine("web window: own window closed, panel closed hwnd=%p", (void*)target); - } else { - if (!IsWindowVisible(target)) ShowWindow(target, SW_SHOW); - LogLine("web window: own window closed, panel restored hwnd=%p", (void*)target); + // 控制台开着:原面板全程压制,一露头就藏,绝不让旧 IE 页面出现在画面上。 + if (ownShown) { + if (!OwnWindowVisible(mode)) { + // 玩家把控制台关了:直接把原面板关掉并进入抑制期,不再放回游戏, + // 这样游戏自己的关闭动画不会把旧页面又画出来(残留)。 + ownShown = false; + if (panel) { + if (panelVisible) ShowWindow(panel, SW_HIDE); + if (g_cfg.closePanel) { + PostMessageW(panel, WM_CLOSE, 0, 0); + suppressUntil = now + 2500; + closeAttempts = 1; + LogLine("web window: console closed, panel closed hwnd=%p", (void*)panel); + } else { + ShowWindow(panel, SW_SHOW); + LogLine("web window: console closed, panel restored hwnd=%p", (void*)panel); + } + } + RestoreGameFromConsole(); + continue; } - consumedPanel = target; - hiddenPanel = nullptr; - RestoreGameFromConsole(); - continue; - } - if (mine) { - ownShown = true; + if (panelVisible) { + ShowWindow(panel, SW_HIDE); + LogLine("web window: panel suppressed hwnd=%p", (void*)panel); + } // 全屏游戏会抢窗口栈,每轮确认一次置顶 GmWindowAssertTop(); - if (g_cfg.suppress && IsWindowVisible(panel)) { - ShowWindow(panel, SW_HIDE); - hiddenPanel = panel; - LogLine("web window: panel hidden hwnd=%p", (void*)panel); - } continue; } - // 这一块面板玩家已经关过一次了,别再自动弹,等他重新点小井盖 - if (consumedPanel == panel) continue; + // 抑制期:面板只要还露着就继续藏、继续请它关闭,期间不重新弹控制台。 + if (suppressUntil) { + if (!panel || now > suppressUntil) { + suppressUntil = 0; + closeAttempts = 0; + } else { + if (panelVisible) { + ShowWindow(panel, SW_HIDE); + if (g_cfg.closePanel && closeAttempts < 4) { + PostMessageW(panel, WM_CLOSE, 0, 0); + ++closeAttempts; + } + LogLine("web window: panel re-hidden after close hwnd=%p attempts=%d", + (void*)panel, closeAttempts); + } + continue; + } + } + + // 面板不存在,或者存在但被游戏自己藏着 —— 都不该弹控制台。 + // 只有「玩家点了小井盖、面板真的显示出来」才算一次打开请求。 + if (!panelVisible) continue; if (mode == MODE_GM_WINDOW) { if (GmWindowFailed()) { @@ -716,16 +733,15 @@ DWORD WINAPI WorkerThread(void*) mode = MODE_APP_WINDOW; continue; } + // 先藏面板再建窗口,避免旧页面在中间闪一下 + ShowWindow(panel, SW_HIDE); if (GmWindowShow(g_cfg.url.c_str(), g_cfg.width, g_cfg.height)) { ownShown = true; - if (g_cfg.suppress && IsWindowVisible(panel)) { - ShowWindow(panel, SW_HIDE); - hiddenPanel = panel; - LogLine("web window: panel hidden while showing hwnd=%p", (void*)panel); - } // 独占全屏时先让游戏让位,否则控制台会被游戏画面压住 MakeRoomForConsole(); - LogLine("web window: gm window shown"); + LogLine("web window: gm window shown, panel suppressed hwnd=%p", (void*)panel); + } else { + LogLine("web window: gm window show failed hwnd=%p", (void*)panel); } continue; } @@ -733,12 +749,10 @@ DWORD WINAPI WorkerThread(void*) // MODE_APP_WINDOW if (now - lastLaunch > 3000) { lastLaunch = now; + ShowWindow(panel, SW_HIDE); if (LaunchOwnWindow()) { ownShown = true; - if (g_cfg.suppress && IsWindowVisible(panel)) { - ShowWindow(panel, SW_HIDE); - hiddenPanel = panel; - } + ShowWindow(panel, SW_HIDE); LogLine("web window: app window launched"); } else { mode = MODE_INLINE; diff --git a/server/internal/services/dnfbridge/client_build_id.go b/server/internal/services/dnfbridge/client_build_id.go index 2812574..585eb5b 100644 --- a/server/internal/services/dnfbridge/client_build_id.go +++ b/server/internal/services/dnfbridge/client_build_id.go @@ -10,7 +10,7 @@ const ( boundMovieSHA256 = "D62E3623C39CD9FCED5F6C4C7F5E6E18BEB69EB269ABF5D713EDE08EE8C926A1" boundMovieBytes = int64(22846532) - boundDllSHA256 = "7A179B1BE8348F4582F0DF89E6E493AEA105FAD0C19D8AC09517EA850788F1E3" + boundDllSHA256 = "CE86D4DF60BC3F8CB33F31D8D2FFE94512FA0E3B7D2EF24B13E142507787CC42" boundDllBytes = int64(1258496) )