fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare 40250 phase switch values with the classic session gates.
|
||||
|
||||
The Win32 source has three kinds of cases that must not be confused: phase
|
||||
payload handlers, transport/control packets consumed by ClassicStream, and
|
||||
observer-only cases that are not in the main packet registry. Compare numeric
|
||||
wire values so enum aliases cannot hide a missing branch.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
CONTROL_VALUES = {44, 152, 153, 250, 251, 252, 253, 254, 255}
|
||||
OBSERVER_VALUES = {96, 97, 98}
|
||||
|
||||
|
||||
def active_lines(text: str, *, gaiden: bool = False) -> list[str]:
|
||||
out: list[str] = []
|
||||
stack: list[bool] = []
|
||||
enabled = True
|
||||
for line in text.splitlines():
|
||||
stripped = line.strip()
|
||||
if stripped in {"#if defined(GAIDEN)", "#ifdef GAIDEN"}:
|
||||
stack.append(enabled)
|
||||
enabled = enabled and gaiden
|
||||
continue
|
||||
if stripped == "#ifndef GAIDEN":
|
||||
stack.append(enabled)
|
||||
enabled = enabled and not gaiden
|
||||
continue
|
||||
if stripped == "#else" and stack:
|
||||
parent = stack[-1]
|
||||
enabled = parent and not enabled
|
||||
continue
|
||||
if stripped.startswith("#endif") and stack:
|
||||
enabled = stack.pop()
|
||||
continue
|
||||
if enabled and not stripped.startswith("//"):
|
||||
out.append(line)
|
||||
return out
|
||||
|
||||
|
||||
def values(path: Path, prefix: str) -> dict[str, int]:
|
||||
result: dict[str, int] = {}
|
||||
pattern = re.compile(rf"\b({re.escape(prefix)}[A-Z0-9_]+)\s*=\s*(0x[0-9A-Fa-f]+|\d+)")
|
||||
for line in active_lines(path.read_text(errors="replace")):
|
||||
match = pattern.search(line)
|
||||
if match:
|
||||
result.setdefault(match.group(1), int(match.group(2), 0))
|
||||
return result
|
||||
|
||||
|
||||
def function_body(text: str, name: str) -> str:
|
||||
match = re.search(rf"\bbool\s+{re.escape(name)}\s*\([^)]*\)\s*\{{", text)
|
||||
if not match:
|
||||
raise ValueError(f"cannot find {name}")
|
||||
start = match.end()
|
||||
depth = 1
|
||||
index = start
|
||||
while index < len(text) and depth:
|
||||
if text[index] == "{":
|
||||
depth += 1
|
||||
elif text[index] == "}":
|
||||
depth -= 1
|
||||
index += 1
|
||||
if depth:
|
||||
raise ValueError(f"unterminated {name}")
|
||||
return text[start:index - 1]
|
||||
|
||||
|
||||
def case_values(body: str, prefix: str, table: dict[str, int]) -> set[int]:
|
||||
result: set[int] = set()
|
||||
for name in re.findall(rf"\b({re.escape(prefix)}[A-Z0-9_]+)\s*:", body):
|
||||
if name in table:
|
||||
result.add(table[name])
|
||||
return result
|
||||
|
||||
|
||||
def reference_phase_values(root: Path, filename: str) -> set[int]:
|
||||
text = (root / "UserInterface" / filename).read_text(errors="replace")
|
||||
enum_values = values(root / "UserInterface" / "Packet.h", "HEADER_GC_")
|
||||
return case_values(text, "HEADER_GC_", enum_values)
|
||||
|
||||
|
||||
def audit(repo_root: Path) -> dict:
|
||||
reference_root = repo_root.parent / "40250/Server Client TMP4/ClientVS22/source"
|
||||
wire = repo_root / "extension/src/net/classic/wire_classic.h"
|
||||
session_path = repo_root / "extension/src/net/classic/classic_session.cpp"
|
||||
reference_phases = {
|
||||
"login": reference_phase_values(reference_root, "PythonNetworkStreamPhaseLogin.cpp"),
|
||||
"select": reference_phase_values(reference_root, "PythonNetworkStreamPhaseSelect.cpp"),
|
||||
"loading": reference_phase_values(reference_root, "PythonNetworkStreamPhaseLoading.cpp"),
|
||||
"game": reference_phase_values(reference_root, "PythonNetworkStreamPhaseGame.cpp"),
|
||||
}
|
||||
current_values = values(wire, "HDR_GC_")
|
||||
session_text = session_path.read_text(errors="replace")
|
||||
current_phases = {
|
||||
"login": case_values(function_body(session_text, "login_phase_packet"), "HDR_GC_", current_values),
|
||||
"select": case_values(function_body(session_text, "select_phase_packet"), "HDR_GC_", current_values),
|
||||
"loading": case_values(function_body(session_text, "loading_phase_packet"), "HDR_GC_", current_values),
|
||||
"game": case_values(function_body(session_text, "game_phase_packet"), "HDR_GC_", current_values),
|
||||
}
|
||||
differences: dict[str, dict[str, list[int]]] = {}
|
||||
for phase, reference in reference_phases.items():
|
||||
ignored = CONTROL_VALUES | (OBSERVER_VALUES if phase == "game" else set())
|
||||
expected = reference - ignored
|
||||
actual = current_phases[phase] - ignored
|
||||
differences[phase] = {
|
||||
"reference_payload_not_current": sorted(expected - actual),
|
||||
"current_not_reference_payload": sorted(actual - expected),
|
||||
}
|
||||
status = "PASS" if all(not values for row in differences.values() for values in row.values()) else "PARTIAL"
|
||||
return {
|
||||
"status": status,
|
||||
"ignored_transport_control_values": sorted(CONTROL_VALUES),
|
||||
"ignored_observer_values": sorted(OBSERVER_VALUES),
|
||||
"reference_case_counts": {phase: len(items) for phase, items in reference_phases.items()},
|
||||
"current_case_counts": {phase: len(items) for phase, items in current_phases.items()},
|
||||
"differences": differences,
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--repo-root", type=Path, default=Path(__file__).resolve().parents[4])
|
||||
parser.add_argument("--strict", action="store_true")
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
result = audit(args.repo_root.resolve())
|
||||
except (OSError, ValueError) as exc:
|
||||
print(json.dumps({"status": "ERROR", "error": str(exc)}, indent=2, sort_keys=True))
|
||||
return 2
|
||||
print(json.dumps(result, indent=2, sort_keys=True))
|
||||
return 1 if args.strict and result["status"] != "PASS" else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user