feat: WaHappy v0 —— 舞萌DX 纯网络游戏助手(占位版)
当前仅 upsert.py 可用:扫码登录 -> 上传一局成绩 -> 落库回查 -> 登出。 - api/ 下 40+ 个接口模块已实现,236 项零网络自检全绿 - 上传成绩链路已真机跑通,报文与真机抓包逐字节一致 - 开发文档见 docs 分支;source/ 与 logs/ 不入库
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
gamesettings.py —— 获取游戏设置(GetGameSettingApi)
|
||||
================================================================================
|
||||
|
||||
功能:取服务器下发的运行时配置(维护窗口、心跳间隔、包超时、各类上限)。
|
||||
|
||||
对应接口:GetGameSettingApi
|
||||
请求体 :{"placeId": <店铺ID>, "clientId": "<机台短值>"}
|
||||
source/Assembly-CSharp/Net.VO.Mai2/GameSettingRequestVO.cs
|
||||
source/Assembly-CSharp/Net.Packet.Mai2/PacketGetGameSetting.cs:21-24
|
||||
|
||||
★ 真机调用时机:会话最开始、Ping 之前(source/2026-10-05.log:1)。
|
||||
|
||||
调用示例:
|
||||
from api import GetGameSettings
|
||||
resp = GetGameSettings()
|
||||
print(resp.gameSetting.isMaintenance, resp.gameSetting.requestInterval)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
from config import CLIENT_ID, PLACE_ID, TIMEOUT_MS
|
||||
|
||||
from .log_setup import log_info, log_warning
|
||||
from .maimai_net import MaimaiNetClient, request_dict
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# VO(字段对照 source/Assembly-CSharp/Net.VO.Mai2/GameSetting.cs 与真机响应)
|
||||
# =============================================================================
|
||||
|
||||
@dataclass
|
||||
class GameSetting:
|
||||
"""游戏运行时配置(对应 Net.VO.Mai2.GameSetting)。"""
|
||||
isMaintenance: bool = False
|
||||
requestInterval: int = 0
|
||||
rebootStartTime: str = ""
|
||||
rebootEndTime: str = ""
|
||||
rebootInterval: int = 0
|
||||
movieUploadLimit: int = 0
|
||||
movieStatus: int = 0
|
||||
movieServerUri: str = ""
|
||||
deliverServerUri: str = ""
|
||||
oldServerUri: str = ""
|
||||
usbDlServerUri: str = ""
|
||||
maxCountRivalMusic: int = 0
|
||||
replicationDelayLimit: int = 0
|
||||
exclusionStartTime: str = ""
|
||||
exclusionEndTime: str = ""
|
||||
pingDisable: bool = False
|
||||
packetTimeout: int = 0
|
||||
packetTimeoutLong: int = 0
|
||||
packetRetryCount: int = 0
|
||||
userDataDlErrTimeout: int = 0
|
||||
userDataDlErrRetryCount: int = 0
|
||||
userDataDlErrSamePacketRetryCount: int = 0
|
||||
userDataUpSkipTimeout: int = 0
|
||||
userDataUpSkipRetryCount: int = 0
|
||||
iconPhotoDisable: bool = False
|
||||
uploadPhotoDisable: bool = False
|
||||
photoDisableArea: Optional[str] = None
|
||||
collectDelayInfoDisable: bool = False
|
||||
maxCountMusic: int = 0
|
||||
maxCountItem: int = 0
|
||||
nameEntryDisable: bool = False
|
||||
packetRecreateCount: int = 0
|
||||
|
||||
|
||||
@dataclass
|
||||
class GameSettingRequestVO:
|
||||
"""请求 VO(对应 GameSettingRequestVO)。"""
|
||||
placeId: int = 0
|
||||
clientId: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
class GameSettingResponseVO:
|
||||
"""响应 VO(对应 GameSettingResponseVO)。"""
|
||||
isAouAccession: bool = False
|
||||
gameSetting: GameSetting = field(default_factory=GameSetting)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 公开 API
|
||||
# =============================================================================
|
||||
|
||||
def GetGameSettings(
|
||||
*,
|
||||
place_id: int = PLACE_ID,
|
||||
client_id: str = CLIENT_ID,
|
||||
timeout_ms: Optional[int] = None,
|
||||
client: Optional[MaimaiNetClient] = None,
|
||||
) -> GameSettingResponseVO:
|
||||
"""
|
||||
获取游戏设置(服务器下发的运行时配置)。
|
||||
|
||||
参数:
|
||||
place_id: 店铺 ID(可选,默认 config.PLACE_ID)
|
||||
client_id: 机台短值(可选,默认 config.CLIENT_ID)
|
||||
timeout_ms: 超时毫秒(可选)
|
||||
client: 可复用的 MaimaiNetClient(可选)
|
||||
|
||||
返回:
|
||||
GameSettingResponseVO
|
||||
"""
|
||||
log_info("获取游戏设置:place_id=" + str(place_id) + " client_id=" + client_id)
|
||||
|
||||
raw = request_dict(
|
||||
"GetGameSettingApi",
|
||||
GameSettingRequestVO(placeId=place_id, clientId=client_id),
|
||||
user_id=0, client_id=client_id,
|
||||
timeout_ms=timeout_ms or TIMEOUT_MS, client=client,
|
||||
)
|
||||
|
||||
gs_raw = raw.get("gameSetting") or {}
|
||||
known = {f for f in GameSetting.__dataclass_fields__}
|
||||
gs = GameSetting(**{k: v for k, v in gs_raw.items() if k in known})
|
||||
resp = GameSettingResponseVO(
|
||||
isAouAccession=bool(raw.get("isAouAccession", False)),
|
||||
gameSetting=gs,
|
||||
)
|
||||
|
||||
if gs.isMaintenance:
|
||||
log_warning("服务器处于维护状态(isMaintenance=True)")
|
||||
if gs.pingDisable:
|
||||
log_info("服务器已禁用心跳(pingDisable=True)")
|
||||
|
||||
log_info(
|
||||
"游戏设置获取完成:isMaintenance=" + str(gs.isMaintenance)
|
||||
+ " requestInterval=" + str(gs.requestInterval)
|
||||
+ " packetTimeout=" + str(gs.packetTimeout)
|
||||
+ " packetTimeoutLong=" + str(gs.packetTimeoutLong)
|
||||
+ " pingDisable=" + str(gs.pingDisable)
|
||||
)
|
||||
return resp
|
||||
|
||||
|
||||
def is_maintenance(settings: GameSettingResponseVO) -> bool:
|
||||
"""便捷判断:服务器是否处于维护状态。"""
|
||||
return bool(settings.gameSetting.isMaintenance)
|
||||
|
||||
|
||||
def is_ping_disabled(settings: GameSettingResponseVO) -> bool:
|
||||
"""便捷判断:是否已禁用心跳。"""
|
||||
return bool(settings.gameSetting.pingDisable)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 单独运行(调试)
|
||||
# =============================================================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
from .log_setup import setup_logging, teardown_logging
|
||||
|
||||
def _main() -> int:
|
||||
setup_logging("gamesettings_test")
|
||||
try:
|
||||
resp = GetGameSettings()
|
||||
gs = resp.gameSetting
|
||||
print("")
|
||||
for name in GameSetting.__dataclass_fields__:
|
||||
print(" " + name.ljust(34) + "= " + str(getattr(gs, name)))
|
||||
return 0
|
||||
except Exception as exc:
|
||||
from .log_setup import log_error
|
||||
log_error("获取游戏设置失败:" + str(exc))
|
||||
return 1
|
||||
finally:
|
||||
teardown_logging()
|
||||
|
||||
raise SystemExit(_main())
|
||||
Reference in New Issue
Block a user