Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 61 additions & 8 deletions app/MaaFW/ArknightWin32.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@


import os
import math
import time
import ctypes
import psutil
Expand All @@ -46,13 +47,44 @@

logger = get_logger("明日方舟PC工具")

# 定时任务每秒触发一次,连接失败后若不退避就会以秒级频率反复重试并刷屏;
# 上限取 60 秒,保证用户随时进入游戏后仍能在一分钟内自动接上。
CONNECT_RETRY_BASE_SECONDS = 2.0
CONNECT_RETRY_MAX_SECONDS = 60.0
# 指数要先封顶:失败次数一直累加时 2 ** (failures - 1) 会在第 1025 次转 float 溢出,
# 异常从 except 块里抛出会直接终止每秒定时任务;封顶到刚越过上限的那一档即可。
_CONNECT_RETRY_MAX_EXPONENT = math.ceil(
math.log2(CONNECT_RETRY_MAX_SECONDS / CONNECT_RETRY_BASE_SECONDS)
)


def connect_retry_delay(failures: int) -> float:
"""
按连续失败次数计算下次重试前的等待秒数

Args:
failures: 已连续失败的次数, 首次失败传 1

Returns:
float: 等待秒数, 指数增长并封顶到 ``CONNECT_RETRY_MAX_SECONDS``
"""

exponent = min(max(failures - 1, 0), _CONNECT_RETRY_MAX_EXPONENT)
return min(
CONNECT_RETRY_BASE_SECONDS * 2**exponent,
CONNECT_RETRY_MAX_SECONDS,
)
Comment on lines +73 to +76

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): 当连续失败次数达到 1025 次时,2 ** (failures - 1) 在与浮点数相乘时转换为 float,直接抛出 OverflowError,因此异常处理本身失败,second_task 终止且后续不再自动重连。60 秒封顶并不能避免这个问题,因为指数表达式是在 min 执行前计算的。

Triggers: 当明日方舟窗口持续无法连接约 17 小时以上,累计失败次数达到 1025 次。

Suggested fix: 在指数运算前先按封顶阈值截断失败次数,或在 failures 达到对应阈值时直接返回 CONNECT_RETRY_MAX_SECONDS

Suggested change
return min(
CONNECT_RETRY_BASE_SECONDS * 2 ** max(failures - 1, 0),
CONNECT_RETRY_MAX_SECONDS,
)
return min(
CONNECT_RETRY_BASE_SECONDS * 2 ** min(max(failures - 1, 0), 5),
CONNECT_RETRY_MAX_SECONDS,
)
Original comment in English

issue (bug_risk): 当连续失败次数达到 1025 次时,2 ** (failures - 1) 在与浮点数相乘时转换为 float,直接抛出 OverflowError,因此异常处理本身失败,second_task 终止且后续不再自动重连。60 秒封顶并不能避免这个问题,因为指数表达式是在 min 执行前计算的。

Triggers: 当明日方舟窗口持续无法连接约 17 小时以上,累计失败次数达到 1025 次。

Suggested fix: 在指数运算前先按封顶阈值截断失败次数,或在 failures 达到对应阈值时直接返回 CONNECT_RETRY_MAX_SECONDS

Suggested change
return min(
CONNECT_RETRY_BASE_SECONDS * 2 ** max(failures - 1, 0),
CONNECT_RETRY_MAX_SECONDS,
)
return min(
CONNECT_RETRY_BASE_SECONDS * 2 ** min(max(failures - 1, 0), 5),
CONNECT_RETRY_MAX_SECONDS,
)



class _ArknightWin32Toolkit:
def __init__(self):

self.arknights_hwnd = -1
self.arknights_window = None

self.connect_failures = 0
self.next_connect_attempt = 0.0

self.tasker = Tasker()
self.listener = keyboard.Listener()

Expand Down Expand Up @@ -101,6 +133,8 @@ async def scheduled_task(self) -> None:

if self.arknights_hwnd != new_hwnd:
self.arknights_hwnd = new_hwnd
# 窗口发生变化意味着新的连接机会,清掉上一轮的退避
self.reset_connect_backoff()

if new_hwnd == 0:
logger.warning("未检测到明日方舟窗口,暂停任务器")
Expand All @@ -110,9 +144,19 @@ async def scheduled_task(self) -> None:
else:
await self.connect_arknights()

if not self.get_connect_status() and self.arknights_hwnd > 0:
if (
not self.get_connect_status()
and self.arknights_hwnd > 0
and time.monotonic() >= self.next_connect_attempt
):
await self.connect_arknights()

def reset_connect_backoff(self) -> None:
"""清空连接失败计数与退避窗口"""

self.connect_failures = 0
self.next_connect_attempt = 0.0

def get_connect_status(self) -> bool:
"""获取连接状态"""

Expand Down Expand Up @@ -144,15 +188,24 @@ async def connect_arknights(self) -> None:
keyboard_method=MaaWin32InputMethodEnum.Seize,
)
logger.success("已连接到明日方舟")
self.reset_connect_backoff()
except Exception as e:
logger.error(f"连接明日方舟失败: {e}")
await Publisher.send(
id=protocol.ID_ARKNIGHTS_PC_TOOLKIT,
type=protocol.TOOLKIT_NOTICE,
data=WSTaskNoticeData(
level="error", message=f"无法连接明日方舟: {str(e)}"
),
self.connect_failures += 1
delay = connect_retry_delay(self.connect_failures)
self.next_connect_attempt = time.monotonic() + delay
logger.error(
f"连接明日方舟失败(第 {self.connect_failures} 次,"
f"{delay:.0f} 秒后重试): {e}"
)
# 仅首次失败提示用户,避免重试期间反复弹出同一条通知
if self.connect_failures == 1:
await Publisher.send(
id=protocol.ID_ARKNIGHTS_PC_TOOLKIT,
type=protocol.TOOLKIT_NOTICE,
data=WSTaskNoticeData(
level="error", message=f"无法连接明日方舟: {str(e)}"
),
)

def on_key_release(self, key: keyboard.Key | keyboard.KeyCode | None) -> None:
"""pynput 回调"""
Expand Down
3 changes: 2 additions & 1 deletion res/version.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"前端类型治理 清理脚本与 Electron 基础边界中的 any,并修复 BetterGI 设置会话无法接收完成和错误事件的问题",
"HSR专项 修复 MAS 管控任务的文本与 JSON 配置项在输入过程中被重渲染清空、导致只有数字项能填写的问题(如货币战争的首领/投资重开条件、策略文件、开拓者名称) by [@qiyinxi](https://github.com/qiyinxi)",
"OK-WW专项 修复任务报告推送因漏传通知用户配置参数而失败的问题 by [@AthenaHibou](https://github.com/AthenaHibou)",
"游戏签到 修复通知返回值改为 DispatchResult 后未同步两处消费方、开启签到通知时执行签到必报 TypeError 的问题(手动签到接口返回 500,慢渠道后台通知路径丢失日志) by [@1w1w11w1](https://github.com/1w1w11w1)"
"游戏签到 修复通知返回值改为 DispatchResult 后未同步两处消费方、开启签到通知时执行签到必报 TypeError 的问题(手动签到接口返回 500,慢渠道后台通知路径丢失日志) by [@1w1w11w1](https://github.com/1w1w11w1)",
"修复明日方舟PC工具连接失败后每秒重试并反复弹出错误提示的问题"
]
},
"v5.5.0-beta.2": {
Expand Down
Loading