Files
mtgodot-poc/audit/manifest.json
T
shenleiandClaude Sonnet 5 c5c183da37 fix(movement): remove unreferenced [0.25,3.0] clamp on local player's movSpd scale
CInstanceBase::SetMoveSpeed only guards moving_speed > 1100 -> 0; the local
player's set_server_speed() was additionally floor/ceil-clamping the scale to
an arbitrary [0.25, 3.0] band with no basis in the reference or in this
project's already-fixed remote-entity equivalent (EntityStore::motion_move_speed).
A heavy haste stack or slow debuff on the local player would silently diverge
from what everyone else sees. Removed the extra clamp; kept the moving_speed<=0
early-return guard against pre-spawn/malformed values.

audit: movement.keyboard.motion/server-speed-scale-clamp closed with reference
citation, regression test, and full related-suite pass; contract stays PARTIAL.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 16:31:17 +09:00

4642 lines
220 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"schema_version": 1,
"reference_root": "../40250/Server Client TMP4/ClientVS22/source",
"contracts": [
{
"id": "network.login.phase_flow",
"title": "登录、握手、角色选择和进入游戏阶段流转",
"subsystem": "network",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/AccountConnector.cpp",
"UserInterface/PythonNetworkStreamPhaseLogin.cpp",
"UserInterface/PythonNetworkStreamPhaseHandShake.cpp",
"UserInterface/PythonNetworkStreamPhaseSelect.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamPhaseLoading.cpp",
"UserInterface/PythonNetworkStream.cpp",
"EterLib/NetStream.cpp"
],
"symbols": [
"CAccountConnector::Connect",
"CAccountConnector::__AuthState_RecvPhase",
"CAccountConnector::OnConnectFailure",
"CPythonNetworkStream::ConnectGameServer",
"CPythonNetworkStream::RecvPhasePacket",
"CPythonNetworkStream::SetHandShakePhase",
"CPythonNetworkStream::SetLoginPhase",
"CPythonNetworkStream::SetSelectPhase",
"CPythonNetworkStream::SetLoadingPhase",
"CPythonNetworkStream::SendEnterGame",
"CPythonNetworkStream::SetGamePhase",
"CPythonNetworkStream::SendLoginPacket",
"CPythonNetworkStream::SendLoginPacketNew",
"CPythonNetworkStream::OnConnectFailure"
]
},
"implementation": {
"files": [
"project/app_flow.gd",
"extension/src/net/m2_client.cpp",
"extension/src/net/m2_client.h",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_session.h",
"extension/src/net/classic/classic_stream.cpp",
"extension/src/net/classic/classic_stream.h",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/classic_parser.h",
"extension/src/net/auth_client.h",
"project/ui/loading_screen.gd",
"project/ui/reconnect_ui.gd",
"project/disconnect_world_reset_test.gd",
"extension/src/net/text_codec.h",
"extension/src/net/text_codec.cpp",
"extension/src/net/classic/wire_classic.h",
"extension/tests/net_text_codec_test.cpp",
"extension/tools/net_classic_e2e.cpp"
],
"symbols": [
"AppFlow::_do_connect",
"AppFlow::_enter_character",
"M2Client::connect_to_server",
"M2Client::retry_login",
"M2Client::disconnect_from_server",
"M2Client::pump_auth",
"M2Client::pump_classic",
"M2Client::pump_game",
"M2Client::reconnect",
"ClassicSession::connect",
"ClassicSession::retry_login",
"ClassicSession::connect_with_login_key",
"ClassicSession::connect_direct_enter",
"ClassicSession::on_auth_phase",
"ClassicSession::on_auth_packet",
"ClassicSession::on_phase",
"ClassicSession::on_packet",
"ClassicSession::send_auth_login",
"ClassicSession::send_login",
"ClassicSession::send_login_by_key",
"ClassicSession::send_select_char",
"ClassicSession::enter_game",
"ClassicStream::process",
"ClassicStream::handle_control",
"ClassicStream::dispatch",
"ClassicParser::on_gc",
"mtnet::to_wire",
"mtnet::from_wire_str",
"mtnet::wire_text_fits",
"AuthClient::on_phase",
"AuthClient::on_packet"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
{
"reference": "CPythonNetworkStream/AccountConnector phase callbacks and EterLib socket pump",
"implementation": "ClassicSession/ClassicStream plus M2Client Godot signals",
"invariant": "auth key, selected slot, phase order, packet side effects and failure cleanup remain equivalent",
"tests": [
"build/extension/net_classic_session_test",
"build/extension/net_loopback_test"
]
}
],
"evidence": {
"contract": "audit/contracts/network.login.phase-flow.md",
"tests": [
"project/live_smoke_test.gd",
"project/app_flow_lifecycle_test.gd",
"project/app_flow_lifecycle_test.gd (login failure UI owner)",
"build/extension/net_classic_session_test",
"build/extension/net_classic_stream_test",
"build/extension/net_loopback_test",
"project/p10_test.gd",
"project/disconnect_world_reset_test.gd",
"build/extension/net_text_codec_test",
"ctest --test-dir build --output-on-failure -j2",
"build/extension/net_classic_e2e <redacted-live-account>",
"build/extension/net_classic_e2e <redacted-live-account-invalid-password> (expected login callback exit 5)",
"build/extension/net_classic_e2e <redacted-live-account-unavailable-game-port> (expected connect-failed callback exit 5)",
"build-debug/extension/net_classic_e2e <redacted-live-account> -> configured game channel (PASS: auth -> four slots -> select -> loading -> PHASE_GAME)",
"build-debug/extension/net_classic_e2e <redacted-live-account> with MT_CLASSIC_EXPECT_EMPTY=1 (PASS as expected refusal: slot 0 occupied; probe does not misclassify a partially empty list as an all-empty account)",
"build-debug/extension/net_classic_e2e <redacted-live-account> -> configured game channel with MT_CLASSIC_DIRECT=1 (PASS: DirectEnter CG_PLAYER_SELECT -> PHASE_LOADING -> PHASE_GAME; 21 entities, 21 inventory slots)",
"build-debug/extension/net_classic_e2e <redacted-live-account> -> MT_CLASSIC_WARP_COMMAND=/warp (PASS: real GC_WARP replacement CG_LOGIN2 -> CG_PLAYER_SELECT -> PHASE_GAME)",
"project/live_smoke_test.gd with MT_SMOKE_WARP_COMMAND=/warp (PASS: M2Client world_reset and second entered_game; live report)",
"build-debug/extension/net_classic_e2e <redacted-live-account-invalid-password> -> configured game channel (PASS as expected probe result: WRONGPWD callback, exit 5)",
"build-debug/extension/net_classic_e2e <redacted-live-account> -> unavailable game channel (PASS as expected probe result: connect-failed callback, exit 5)",
"net_classic_session_test (auth peer close -> Offline owner)",
"net_classic_session_test (HybridCrypt deployment guard and phase-owner retention)",
"project/app_flow_lifecycle_test.gd (PHASE_CLOSE reconnect owner and phase-only LoginPhase fallback)",
"project/app_flow_lifecycle_test.gd (explicit PHASE_CLOSE does not synthesize transport disconnected)",
"net_classic_session_test (Loading PHASE_CLOSE leave owner and world cleanup)",
"net_classic_session_test (PHASE_CLOSE clears the old transport and does not send CG_LOGIN on the closed stream)",
"project/app_flow_lifecycle_test.gd (GC_WARP and fast reconnect failure retry-owner invariant)",
"project/app_flow_lifecycle_test.gd (empty reconnect list returns to SELECT owner without phantom select)",
"project/app_flow_lifecycle_test.gd (pre-game LoginPhase transition replaces SELECT owner)",
"project/app_flow_lifecycle_test.gd (LoginPhase retry uses retry_login owner without full connect teardown)",
"net_classic_session_test (LoginPhase retry replaces auth transport and sends one new CG_LOGIN3)",
"net_classic_session_test (game peer-close retains the same session retry owner, replaces auth transport, and sends one new CG_LOGIN3 with the new password)",
"net_classic_session_test (GC_AUTH_SUCCESS clears a pipelined stale PHASE_AUTH without duplicate CG_LOGIN3)",
"net_classic_session_test pre-fix GC_AUTH_SUCCESS result authority (FAIL: result=1 with login_key=0 was reported as BESAMEKEY; 1 check)",
"net_classic_session_test post-fix GC_AUTH_SUCCESS result authority (PASS: only result/bResult selects rejection; auth transport still cleans up for result=1/login_key=0)",
"net_classic_session_test pre-fix PHASE_HANDSHAKE re-entry (FAIL: game/auth owners ignored the phase and game world leave cleanup; 3 checks)",
"net_classic_session_test post-fix PHASE_HANDSHAKE re-entry (PASS: game/auth owners return to HandShake, Game world is cleared before on_phase_leave)",
"net_classic_session_test (LoginPhase returns after GC_LOGIN_FAILURE and defers a pipelined PHASE_LOGIN to the next tick)",
"net_classic_session_test (synchronous auth connect failure returns to Offline and keeps the LoginPhase retry owner)",
"project/auth_connect_bridge_test.gd (initial and retry auth connect failure use connect_failed, retain a non-terminal LoginPhase owner, and do not emit login_failed)",
"project/auth_peer_close_bridge_test.gd (initial and retry auth peer close retain a non-terminal LoginPhase owner without login_failed or connect_failed)",
"net_classic_session_test (DirectEnter game connect failure routes through ClosePhase without credential login failure)",
"net_classic_session_test (ordinary auth-to-game connect failure uses the distinct connect-failure callback)",
"project/app_flow_lifecycle_test.gd (connect_failed keeps LoginPhase UI and status separate from login_failed)",
"net_classic_session_test (DirectEnter PHASE_SELECT emits CG_PLAYER_SELECT before Loading)",
"project/app_flow_lifecycle_test.gd and project/p10_test.gd (peer-close/phase-only LoginPhase owner releases GameScene and does not retain ReconnectUI)",
"redacted Godot live smoke with MT_SMOKE_PHASE_COMMAND=/logout (PASS functional report: PHASE_GAME -> peer close -> LoginPhase owner; outer log gate retains unrelated shader leak)",
"redacted Godot live smoke with MT_SMOKE_PHASE_COMMAND=/logout and MT_NET_TRACE=1 (PASS functional report; no client-observable phase=0/GC_PHASE(PHASE_CLOSE), server fixture produced peer close only)",
"net_classic_session_test pre-fix auth-success handoff order (FAIL: game transport must start before auth transport clear; 1 check)",
"net_classic_session_test post-fix auth-success handoff order (PASS: game:connecting -> auth:offline on loopback transports)",
"net_classic_session_test pre-fix game-login retry process boundary (FAIL: replacement PHASE_LOGIN was consumed in the auth-success frame; 1 check)",
"net_classic_session_test post-fix game-login retry process boundary (PASS: replacement game transport is processed on the following pump)",
"net_text_codec_test (PASS: 龙驹简体中文免费版 is 27 UTF-8 bytes but 18 GB2312 bytes and round-trips through the 24-byte field)",
"project/char_create_delete_test.gd (PASS: CharSelectScreen does not reject the GB2312-fitting Chinese name before native conversion)",
"net_classic_session_test pre-fix Loading EnterGame event boundary (FAIL: GC_MAIN_CHARACTER did not trigger CG_ENTERGAME when the compatibility timer was disabled; 1 check)",
"net_classic_session_test post-fix Loading EnterGame event boundary (PASS: GC_MAIN_CHARACTER sends CG_CLIENT_VERSION2 then one sequenced CG_ENTERGAME; duplicate main-character packets remain idempotent)",
"net_classic_session_test pre-fix DirectEnter failure login-mode reset (FAIL: DirectEnter connect failure retained the stale slot through replacement PHASE_LOGIN; 1 check)",
"net_classic_session_test post-fix DirectEnter failure login-mode reset (PASS: ClosePhase owner clears DirectEnter and ordinary replacement PHASE_LOGIN clears the stale slot)",
"net_classic_session_test pre-fix PHASE_CLOSE LoginWindow retry owner (FAIL: PHASE_CLOSE did not retain a retry owner for replacement auth transport; 1 check)",
"net_classic_session_test post-fix PHASE_CLOSE LoginWindow retry owner (PASS: same session replaces auth transport and sends one new CG_LOGIN3 after PHASE_AUTH)",
"net_classic_session_test pre-fix auth PHASE_CLOSE silent owner (FAIL: auth PHASE_CLOSE was reported as game phase closure/auth server closed phase; 1 check)",
"net_classic_session_test post-fix auth PHASE_CLOSE silent owner (PASS: auth PHASE_CLOSE keeps AUTH owner without phase_closed or synthetic error)",
"40250 PythonNetworkStreamPhaseSelect.cpp reference inspection (PASS: SetSelectPhase runs the previous phase-leave owner before installing SelectPhase)",
"net_classic_session_test pre-fix PHASE_SELECT re-entry owner (FAIL: Game -> Select retained the old world and skipped phase-leave notification; 2 checks)",
"net_classic_session_test post-fix PHASE_SELECT re-entry owner (PASS: Game -> Select clears the old world and notifies the host before installing SelectPhase)",
"40250 PythonNetworkStream.cpp reference inspection (PASS: RecvPhasePacket(PHASE_DEAD) is an explicit no-op)",
"net_classic_session_test pre-fix PHASE_DEAD no-op time-sync owner (FAIL: PHASE_DEAD enabled time-sync and changed the next handshake response; 1 check)",
"net_classic_session_test pre-fix PHASE_DEAD close-owner preservation (FAIL: PHASE_DEAD cleared the explicit PHASE_CLOSE owner marker; 1 check)",
"net_classic_session_test post-fix PHASE_DEAD no-op time-sync owner (PASS: PHASE_DEAD keeps the plain handshake owner and preserves the explicit close marker)"
],
"last_test_result": "PASS"
},
"remaining": [
"network.login.phase_flow/auth-game-phase-matrix/live-package-order:真实服务端成功认证、四槽角色列表、选角、Loading 到 PHASE_GAME、错误密码 WRONGPWD 回调、game connect failure 和一次建立连接后的 peer close 已验证;认证成功后 game transport 先于 auth transport 清理的 40250 handoff 顺序已有 loopback 修复前失败/修复后通过证据;/logout 探针确认当前服务端 build 只产生 peer close,未产生客户端可观察的 GC_PHASE(PHASE_CLOSE),因此服务器主动 PHASE_CLOSE、空角色、完整服务器包序矩阵和跨阶段 retry 仍缺 live fixture;半包、重复 phase、PHASE_CLOSE 旧 world 和 PHASE_CLOSE/peer-close 不发送旧连接登录包的本地测试已覆盖",
"network.login.phase_flow/auth-game-phase-matrix/empty-role-live-fixture:本地全空 LOGIN_SUCCESS4/PHASE_SELECT 和无自动选人已有回归;net_classic_e2e 已增加 MT_CLASSIC_EXPECT_EMPTY=1,当前 live 账号只有部分空槽,正向全空账号证据仍缺",
"network.login.phase_flow/login-failure-retry-cleanup:本地 auth peer close -> Offline、auth peer-close bridge 保留非终态 LoginPhase owner、pre-game/in-game peer close、旧 world 清理、游戏 peer-close 后同一 session 替换认证 transport 并用新密码发送 CG_LOGIN3、DirectEnter/普通 LoginPhase game connect failure owner、认证 TCP connect failure -> Offline、认证失败后的同 session retry、认证成功按 bResult 而非 ticket 值判定、认证 connect_failed bridge、登录表单 retry 只替换认证 transport、半包和重复 phase、普通 Select/Game -> Login 的角色列表/world 清理、Game -> Select 的旧 world 清理和 phase-leave 通知、PHASE_DEAD 保持空操作且不改变 time-sync owner、认证/游戏 PHASE_HANDSHAKE 重入 owner、认证 PHASE_CLOSE 保持 AUTH owner 且不设置游戏 phase_closed、SELECT/Loading 的 phase-only LoginPhase owner、LOGIN 阶段失败保留原 LoginPhase UI、PHASE_AUTH 前忽略提前到达的 GC_LOGIN_FAILURE、认证未知应用头保留 AUTH owner、active Loading/Game 未知头按 RecvErrorPacket 清理缓冲、游戏态 PHASE_CLOSE/peer-close 后 AppFlow 回唯一 LoginPhase owner、显式 PHASE_CLOSE 不再伪造 transport disconnected、显式 PHASE_CLOSE 后同一 session 替换认证 transport、Loading PHASE_CLOSE leave owner/world 清理、GC_WARP/快速重连同步连接失败后的 retry owner,以及空重连角色列表回到 SELECT 且不发送过期槽位已有本地回归;真实 Windows LoginWindow.OnConnectFailure、认证其它可选/静默分支、跨阶段失败后的完整 UI retry/真实服务端重试包序、真实重复进入包序、DirectEnter/GC_WARP 失败回退包序和服务器主动 PHASE_CLOSE live 包序仍需测试;40250 的 m_dwChangingPhaseTime 尚未发现实际 enforced check",
"network.login.phase_flow/ticket-slot-warp-order:本地回归和实时探针已证明 login key、selected slot、DirectEnter、GC_WARP 的 replacement CG_LOGIN2/CG_PLAYER_SELECT 包序,新连接到达 PHASE_GAME 前不会继承旧的在线标记,且 DirectEnter/GC_WARP 失败回到 LoginPhase 时会清除 DirectEnter 模式;仍缺失败后的真实 GC_WARP 回退包序",
"network.login.phase_flow/optional-security-branchesMatrix、Passpod、Panama、HybridCrypt、HS/XTrap 等完整副作用仍未实现;静态/动态可选包已增加显式 deployment guard 和 optional_security_required 信号,不可静默断开",
"network.login.phase_flow/real-server-sequence:真实 Ubuntu 40250 服务端的失败账号、DirectEnter、一次成功 GC_WARP 到 PHASE_GAME 和 /logout 后建立连接的 peer close 已验证;服务端源码存在 SetPhase(PHASE_CLOSE) 状态链,但当前 build 的 live trace 没有客户端可观察 phase=0/GC_PHASE(PHASE_CLOSE),空角色、真实服务器主动 PHASE_CLOSE 包序和完整重试包序仍缺 fixture"
],
"notes": "按 40250 SetLoadingPhase/SetGamePhase 修复 Loading 遮罩阶段归属和无 SceneTree 选人确认吞包;按 ClearLoginInfo 修复 CG_LOGIN/CG_LOGIN3 成功发送后的密码生命周期;auth-game-phase-matrix 修复 PHASE_CLOSE 回 LoginPhase、Loading/Game phase-leave world 清理、宿主 world_reset 通知、重复 phase handler、auth/game 登录拒绝保留 phase 和登录失败回调;本轮 auth-success handoff 按 CAccountConnector::__AuthState_RecvAuthSuccess -> SetLoginKey -> MainStream.Connect -> AccountConnector.Disconnect 固定为 game transport 先连接、auth transport 后清理,并以 loopback 失败前/后顺序回归;login-failure-retry-cleanup 现已补齐认证端 peer close -> Offline owner、认证 peer-close bridge 保留非终态 LoginPhase owner、认证 PHASE_CLOSE 静默保留 AUTH owner、游戏 peer close 前置通知、旧 world 清理、DirectEnter/普通 LoginPhase game connect failure 的独立 owner、认证 connect_failed bridge、显式重试请求保护、普通 Select/Game -> Login 的旧角色/world 清理、SELECT/Loading 的 phase-only LoginPhase owner、LOGIN 阶段失败保留原 LoginPhase UI、LoginPhase retry 只替换认证 transport 且不触发重复全局 world_reset、PHASE_AUTH 前登录包的阶段隔离、active Loading/Game 未知头按 RecvErrorPacket 清理缓冲并保留 phase owner、游戏态 PHASE_CLOSE/peer-close 后 AppFlow 释放 GameScene 并切换为唯一 LoginPhase owner、显式 PHASE_CLOSE 不再伪造 transport disconnected、Loading PHASE_CLOSE leave owner/world 清理、GC_WARP/快速重连同步连接失败后的 retry owner,以及空重连角色列表回到 SELECT 且不发送过期槽位;同步增加 ClassicSession 析构前 stream 回调解绑,避免最终 Offline 通知写入已析构字段;本轮 legacy-text-wire-encoding 固定 UTF-8 内部与 GB2312/兼容 wire 边界,修正 24-byte 角色名容量和 GCLoginSuccess4 329-byte 布局;本轮 live-package-order 让实时探针观察应用层登录失败和 game remote-disconnect 回调,不把 40250 的失败回调误报为 timeout;本轮 ticket-slot-warp-order 修复 GC_WARP 复用 session 时继承旧 PHASE_GAME 在线标记,并用回归测试核对 DirectEnter/GC_WARP 的 CG_LOGIN2 包序和 login key;本轮 direct-enter-loading-select-packet 按 40250 introloading.LoadingWindow.Open 的实际调用链补发 DirectEnter 的 CG_PLAYER_SELECT,实时探针已到达 PHASE_GAME;本轮 gc-warp-live-package-order 新增 e2e/Godot live 验证入口,确认真实 GC_WARP 的 world_reset、replacement CG_LOGIN2/CG_PLAYER_SELECT、Loading 和第二次 PHASE_GAME 成功,未发现生产连接逻辑差异;本轮 optional-security-branches 为静态可选包和 HybridCrypt 动态包增加显式 deployment guard/optional_security_required,保持 phase owner 但不伪装成已实现 vendor runtime;本轮 peer-close-login-owner 依据真实 /logout peer-close 证据,移除 AppFlow 的自动重连遮罩/旧 GameScene 保留,回归 LoginPhase owner;本轮 phase-close-server-fixture 分离显式 PHASE_CLOSE 与 OnRemoteDisconnect,不再由 native bridge 发出伪造的 disconnected 信号;本轮 empty-role-live-fixture 为真实空角色包序增加 MT_CLASSIC_EXPECT_EMPTY=1 探针模式,当前仍缺全空账号正向 live 证据;本轮 login-form-retry-owner 按 40250 LoginWindow.Connect/AccountConnector.Connect 增加 LoginPhase retry owner 和只替换认证 transport 的 native/UI 回归;本轮 game-connect-immediate-failure-owner 按 CPythonNetworkStream::OnConnectFailure 分离普通 LoginWindow.OnConnectFailure 与 DirectEnter ClosePhase,并新增 M2Client connect_failed signalGC_WARP 失败不再伪造 disconnected;本轮 auth-connect-bridge-owner 按 CAccountConnector::OnConnectFailure -> Offline -> LoginWindow 分离认证连接失败和凭据失败,保留 ClassicSession 供 retry_login 复用,并以 auth_connect_bridge_test.gd 固定修复前失败/修复后通过证据;本轮 auth-peer-close-bridge-owner 按 CAccountConnector::OnRemoteDisconnect -> Offline 让认证 peer close 不再伪造 login_failed/connect_failed,并以 auth_peer_close_bridge_test.gd 固定旧/新认证 transport 的回归证据。ClassicStream 的 TCP connect deadline、半包、本地 loopback、文本编码回归、真实成功包序、真实失败回调、DirectEnter CG_PLAYER_SELECT 到 PHASE_GAME、真实 GC_WARP 成功 round trip、auth-peer-close owner、auth-peer-close bridge、登录 UI owner、PHASE_CLOSE 旧 world owner、跨阶段 LoginPhase owner、显式 PHASE_CLOSE transport-owner 分离、空角色本地 SELECT owner、auth handshake owner 隔离、认证 PHASE_CLOSE 静默 owner、Loading unknown-header owner、HybridCrypt guard、Loading PHASE_CLOSE leave owner、warp connect failure retry owner、空重连 SELECT owner、pre-game LoginPhase owner、本轮 peer-close LoginPhase owner、login-form-retry-owner、game-connect-immediate-failure-owner 和 auth-connect-bridge-owner 已有本地证据;空角色全空 live fixture、服务器主动 PHASE_CLOSE live 包序、认证其它完整副作用、跨阶段完整 UI retry/真实重复进入包序、GC_WARP 失败回退包序、真实 Windows LoginWindow.OnConnectFailure 和可选安全 runtime 仍使合同保持 PARTIAL。",
"fingerprints": {
"reference": "79759dc3fcc9e1a4d9b779e53484b1d3abfb2b822e26d1c45d7364e9dba5f7a3",
"implementation": "be1421c9c311c980829da8db8edf39ca930661fca905813b29549598bd457c17",
"tests": "d6d994c08fabd5100f5d9f84e32948ff16aaea4ec2f7b4b8407ff7f2136c9b17"
}
},
{
"id": "movement.keyboard.motion",
"title": "键盘 WASD 输入、转向、移动动作和停止",
"subsystem": "movement",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonPlayerInputKeyboard.cpp",
"UserInterface/PythonPlayerInput.cpp",
"UserInterface/InstanceBaseMovement.cpp",
"UserInterface/InstanceBaseMotion.cpp",
"GameLib/ActorInstanceMotion.cpp",
"GameLib/ActorInstanceMotionEvent.cpp",
"GameLib/GameUtil.cpp",
"EterLib/Camera.cpp",
"GameLib/ActorInstanceBattle.cpp",
"GameLib/ActorInstance.cpp"
],
"symbols": [
"CPythonPlayer",
"CInstanceBase",
"SetMotion",
"Move"
]
},
"implementation": {
"files": [
"project/player_controller.gd",
"project/game_camera.gd",
"project/net_play.gd",
"project/net_world.gd",
"project/test_wasd_steering_parity.gd",
"project/test_no_auto_move_regression.gd",
"project/player_motion_test.gd",
"project/keyboard_motion_timeline_test.gd"
],
"symbols": [
"PlayerController",
"NetPlay",
"NetWorld"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
{
"description": "40250 CPythonPlayer::NEW_MoveToDirection m_isCmrRot 相机跟随:D3D CCamera::Roll 的正方向与 Godot yaw 增大(视线转向左)手性相反,_camera_auto_rotate() 对折叠后的 fRotRat 取正号而非参考实现的负号。",
"invariant": "按住某一方向键且通过移动门时,camera.yaw 持续朝该方向收敛:正前/正后 fRotRat=0(不转),正左/右 fRotRat=±90(最大速率),对角线线性减半;速率恒为 CAMERA_AUTO_ROTATE_SPEED_DEG(20.0)*dt*fRotRat/90,从不反向或越界。",
"test": "project/keyboard_motion_timeline_test.gd"
}
],
"evidence": {
"contract": "audit/contracts/movement.keyboard-motion.md",
"tests": [
"project/test_wasd_steering_parity.gd",
"project/test_no_auto_move_regression.gd",
"project/player_motion_test.gd",
"project/keyboard_motion_timeline_test.gd"
],
"last_test_result": "PASS"
},
"remaining": [
"固定 300 像素 Dst 已按事件创建并在目标到达时消费;锁定/移动技能期间 held WASD 会在解锁后重试;.msa 根运动和当前连续速度/米制推进的时序差异仍未闭合",
"OnMove/OnMoving/OnWaiting/OnStop 与当前每帧 signal、NetPlay 节流的事件源逐项证明",
"同步、私店、受击、死亡、传送、情绪以及多种锁门叠加顺序;移动技能/普通锁定/私店/情绪的 held WASD 恢复已覆盖基础路径",
"真实角色模型/骨骼在快速反向输入时不翻转的模型级测试",
"输入事件到移动请求、停止请求和网络包的完整时间线"
],
"fingerprints": {
"reference": "20c74407914aba18dde8f76f7b86917372a56cc7333b2ca8bfeee76c39e357e4",
"implementation": "ce5cc765134cfa04f7cac39ca268cd7bec773f72ea17b3b529f276fb7226b580",
"tests": "2c79ef6e727717527f54e611629ce15154cb78c785b37ada4970d1156caf1bfd"
}
},
{
"id": "combat.death.exp_drop",
"title": "攻击动作、怪物死亡、经验和掉落链路",
"subsystem": "combat",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamPhaseGameActor.cpp",
"UserInterface/InstanceBaseBattle.cpp",
"UserInterface/InstanceBaseMotion.cpp",
"UserInterface/PythonEffectModule.cpp",
"UserInterface/PythonNetworkStreamPhaseGameItem.cpp",
"UserInterface/PythonPlayer.cpp",
"GameLib/ActorInstance.cpp",
"GameLib/FlyingData.cpp",
"UserInterface/Packet.h"
],
"symbols": [
"RecvDeadPacket",
"RecvPointChange",
"RecvCreateFlyPacket",
"RecvDamageInfoPacket",
"RecvItemGroundAddPacket",
"RecvItemGroundDelPacket",
"CInstanceBase::Die",
"CActorInstance::Die",
"CPythonPlayer::NotifyCharacterDead",
"CPythonPlayer::NotifyDeadMainCharacter"
]
},
"implementation": {
"files": [
"project/net_play.gd",
"project/net_world.gd",
"project/player_controller.gd",
"project/fx/skill_fx.gd",
"extension/src/net/entity_store.cpp",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/m2_client.cpp",
"project/ui/ground_items.gd",
"project/ui/death_ui.gd",
"project/ui/quickbar.gd",
"project/game_scene.gd",
"project/mob_death_drop_test.gd",
"project/combat_parity_test.gd",
"project/playable_combat_test.gd",
"project/netplay_test.gd",
"extension/tests/net_state_queue_test.cpp",
"extension/tests/net_entity_test.cpp"
],
"symbols": [
"NetPlay",
"NetWorld",
"PlayerController",
"SkillFx",
"EntityStore::mut_set_point",
"EntityStore::mut_dead",
"EntityStore::mut_ground_add",
"NetWorld::_on_dead",
"NetWorld::_on_fly",
"NetPlay::_on_points",
"NetPlay::_trigger_exp_gain",
"GroundItems::_on_added"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/combat.death-exp-drop.md",
"tests": [
"project/test_combat_death_pickup_entry_parity.gd",
"project/test_exp_fly_parity.gd",
"project/test_ground_pickup_parity.gd",
"project/mob_death_drop_test.gd",
"project/combat_parity_test.gd",
"project/playable_combat_test.gd",
"project/gamescene_test.gd",
"project/netplay_test.gd",
"extension/tests/net_state_queue_test.cpp",
"extension/tests/net_entity_test.cpp"
],
"last_test_result": "PARTIAL: local-main fallback and indexed-fly actor-instance gate regressions pass; live server packet order and protocol race cases remain unverified"
},
"remaining": [
"主角 GC_DEAD 的 GameOver/复活 UI 和恢复动画与真实服务端包序的一致状态门",
"POINT_* 的真实 effect manager 生命周期、金币浮字资源和全部状态窗口旁路刷新",
"真实服务端攻击→伤害→死亡→POINT_EXP→CREATE_FLY→ITEM_GROUND_ADD 的包序记录",
"重复死亡、MISS、无效 VID、目标先删除、空 owner/重名、先拾取后 DEL、重连 catch-up 和拾取竞争",
"死亡尸体/目标效果/经验球/动作队列在 GC_CHARACTER_DEL、复活和跨图 reset 中的统一清理"
],
"notes": "本轮已按 40250 SendClickItemPacket/GetCloseItem/__OnPressItem 修复 party/anti-flag 所有权、DISTANCE_APPROX/300cm 选择、最高 VID tie-break、骑马固定边界和 150cm 点击入口距离门;并修复 RecvCreateFlyPacket 在本地主角 EntityStore 行暂缺时通过 GetMainInstancePtr 语义回退 _local_node,同时要求远端起点/终点实例存在后才创建 indexed fly。相关回归通过;真实服务端死亡包序、重复/乱序/重连/拾取竞争、死亡资源生命周期和跨图清理仍保持 PARTIAL。",
"fingerprints": {
"reference": "6a3545f5d1f424e1404c3dd6a66f49dfa28a105b639fce5f8009860fa50928b6",
"implementation": "2fd306a9f4b85b78fd75fbffb3512cf1939fed11a3ac11a29f5808d890936565",
"tests": "f066c45415ecd31e61da172c633c1fd7d29f89a8d6a8c5a7a13ca2f47b946af8"
}
},
{
"id": "skill.cast.effect_timing",
"title": "技能施法、动作事件、服务端结果和特效时序",
"subsystem": "skill",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonSkill.cpp",
"UserInterface/PythonPlayerSkill.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/InstanceBaseEffect.cpp",
"EffectLib/EffectInstance.cpp",
"EffectLib/ParticleInstance.cpp",
"GameLib/ActorInstanceMotionEvent.cpp"
],
"symbols": [
"CPythonSkill",
"CPythonPlayer::UseSkill",
"CInstanceBase::SetEffect",
"CActorInstance::OnMotionEvent"
]
},
"implementation": {
"files": [
"project/player_skill.gd",
"project/ui/skill_table.gd",
"project/net_play.gd",
"project/net_world.gd",
"project/fx/skill_fx.gd",
"project/fx/effect_player.gd",
"project/ui/player_view.gd",
"project/game_scene.gd",
"project/ui/quickbar.gd",
"project/remote_motion_splash_test.gd",
"extension/src/metin2_anim.h",
"extension/src/metin2_anim.cpp",
"extension/src/net/entity_store.cpp",
"extension/src/net/m2_client.cpp",
"extension/tests/net_state_queue_test.cpp",
"project/combat_fx_test.gd",
"project/skill_test.gd",
"project/test_skill_state_arg_parity.gd",
"project/skill_motion_loop_test.gd",
"project/test_combat_death_pickup_entry_parity.gd"
],
"symbols": [
"PlayerSkill",
"SkillTable",
"NetPlay",
"SkillFx",
"Effect",
"NetPlay::start_remote_motion_splash",
"NetPlay::_remote_splash_attack_process",
"Metin2AnimPlayer::set_motion_loop_count",
"Metin2AnimPlayer::_process"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/skill.cast-effect-timing.md",
"tests": [
"project/combat_fx_test.gd",
"project/skill_test.gd",
"project/effect_playback_test.gd",
"extension/tests/net_state_queue_test.cpp",
"project/test_skill_state_arg_parity.gd",
"project/skill_motion_loop_test.gd",
"project/net_world_remote_action_test.gd",
"project/remote_motion_splash_test.gd",
"project/player_motion_test.gd",
"project/test_combat_death_pickup_entry_parity.gd",
"project/target_effect_test.gd",
"project/test_no_auto_move_regression.gd",
"project/netplay_test.gd"
],
"last_test_result": "PASS: valid MSA skill lock remains motion-driven until motion_bound(wait); timer fallback remains for headless/missing motion; skill/combat/effect/remote-motion regressions passed"
},
"remaining": [
"按技能类型逐分支对比",
"真实攻击/辅助技能时序测试",
"逐技能 MSA 事件到 MSE 的挂点、颜色、混合、飞行/特殊攻击和服务端结果语义"
],
"fingerprints": {
"reference": "e390db94ec7e4c5d0d6518beff0319fe512af840ebe31d07d6136d48dd21687e",
"implementation": "b46796eab17505be66bb99b605f082d781fbda00b06bde98297dd9cd6faea565",
"tests": "ec3c2c7f212a4d0af4da04590dd61ebefc8e200258877c9cdf45195f8913b107"
}
},
{
"id": "ui.equipment.tooltip",
"title": "查看装备属性面板和普通提示框生命周期",
"subsystem": "ui",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonNetworkStreamPhaseGameItem.cpp",
"UserInterface/PythonItem.cpp",
"UserInterface/PythonItemModule.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonTextTail.cpp"
],
"symbols": [
"RecvItemSetPacket",
"CPythonItem",
"CPythonNetworkStream::SendItemUseToItemPacket"
]
},
"implementation": {
"files": [
"project/ui/view_equipment_ui.gd",
"project/ui/item_tooltip.gd",
"project/ui/item_tooltip_view.gd",
"project/ui/item_tooltip_bridge.gd",
"project/ui/ui_manager.gd",
"project/ui/inventory_ui.gd",
"project/ui/shop_ui.gd",
"project/ui/exchange_ui.gd",
"project/ui/safebox_ui.gd",
"project/ui/mall_ui.gd",
"project/ui/private_shop_ui.gd",
"project/ui/equip_model.gd",
"project/ui/player_view.gd",
"project/view_equipment_ui_test.gd",
"project/item_tooltip_test.gd",
"project/inventory_ui_test.gd",
"project/test_npc_shop_flow.gd",
"project/test_safebox_mall_parity.gd",
"project/p8_test.gd",
"project/private_shop_ui_test.gd"
],
"symbols": [
"ViewEquipmentUI",
"ItemTooltip",
"ItemTooltipView",
"EquipModel"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/ui.equipment-tooltip.md",
"tests": [
"project/view_equipment_ui_test.gd",
"project/item_tooltip_test.gd",
"project/equip_model_test.gd",
"project/inventory_ui_test.gd",
"project/test_npc_shop_flow.gd"
],
"last_test_result": "PASS"
},
"remaining": [
"证明背包、商店、交易、仓库、商城仓库、个人摊位和装备窗口快速切换时的全局单面板生命周期",
"属性来源/槽位/时装层级/窗口边界定位对比",
"重复打开关闭、切换目标、空装备和远程更新测试"
],
"fingerprints": {
"reference": "7dd24059f8bdd75e76798702c0b2e951452232ab65ce20eb91ad155312087026",
"implementation": "db411e1ce47a22608c1d3dbab7ee77105ec68298f9a159611ec66c3bc6ce7fc2",
"tests": "5de299be840bc0d46f5eea3acd55af332dd3e1c71472e9ae428e7762bc7f72b8"
}
},
{
"id": "lifecycle.bootstrap_phase_host",
"title": "启动、阶段属主、窗口和应用生命周期",
"subsystem": "lifecycle",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"EterLib/MSApplication.cpp",
"EterLib/MSWindow.cpp",
"UserInterface/PythonApplication.cpp",
"UserInterface/PythonApplicationEvent.cpp",
"UserInterface/PythonApplicationProcedure.cpp",
"UserInterface/PythonNetworkStream.cpp",
"UserInterface/PythonNetworkStreamPhaseLogin.cpp",
"UserInterface/PythonNetworkStreamPhaseSelect.cpp",
"UserInterface/PythonNetworkStreamPhaseLoading.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamPhaseGameActor.cpp"
],
"symbols": [
"CPythonApplication::Run",
"CPythonApplication::Process",
"CPythonApplication::Destroy",
"CPythonApplication::WindowProcedure",
"CPythonNetworkStream::RecvPhasePacket",
"CPythonNetworkStream::ClosePhase",
"CPythonNetworkStream::SetLoginPhase",
"CPythonNetworkStream::SetSelectPhase",
"CPythonNetworkStream::SetLoadingPhase",
"CPythonNetworkStream::SetGamePhase",
"CPythonNetworkStream::__LeaveGamePhase",
"CPythonNetworkStream::__ClearNetworkActorManager"
]
},
"implementation": {
"files": [
"project/client_main.gd",
"project/app_flow.gd",
"project/login.gd",
"project/app_lifecycle.gd",
"project/game_scene.gd",
"project/audio.gd",
"project/net_play.gd",
"project/net_world.gd",
"project/ui/loading_screen.gd",
"project/ui/death_ui.gd",
"project/ui/reconnect_ui.gd",
"extension/src/net/m2_client.cpp",
"extension/src/net/m2_client.h",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_session.h",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/net_bounds.h"
],
"symbols": [
"client_main::_ready",
"AppFlow::start",
"AppFlow::_goto_login",
"AppFlow::_goto_select",
"AppFlow::_goto_game",
"login::_init",
"login::_connect",
"login::_on_entered_game",
"AppLifecycle::_notification",
"M2Client::_process",
"M2Client::net_poll",
"M2Client::disconnect_from_server",
"M2Client::pump_classic",
"ClassicSession::on_phase",
"ClassicSession::disconnect",
"GameScene::_on_world_reset",
"NetPlay::clear_for_map_change",
"NetWorld::clear_for_map_change",
"EntityStore::reset_for_map_change"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "MAPPED",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
{
"reference": "Windows message loop, WM_ACTIVATEAPP and WM_CLOSE",
"implementation": "Godot MainLoop notifications and AppLifecycle",
"invariant": "phase, socket, input, audio, pause and shutdown side effects must remain ordered and observable",
"tests": [
"project/app_flow_lifecycle_test.gd"
]
}
],
"evidence": {
"contract": "audit/contracts/lifecycle.bootstrap-phase-host.md",
"tests": [
"project/app_flow_lifecycle_test.gd"
],
"last_test_result": "PASS: AppFlow close_requested consumer regression; remaining runtime lifecycle branches are PARTIAL"
},
"remaining": [
"PHASE_DEAD 不进入 phase_changed deadDeathUI 已改为只消费 GC_DEAD -> entity_dead,仍需补齐 40250 GameOver/决斗例外、死亡动作和真实包序",
"PHASE_CLOSE 的本地 ClassicSession/AppFlow owner 已按 ClosePhase -> SetLoginPhase 回到 Login;仍需补齐真实 runtime 的状态/UI 时序、重复 close 和最终清理证据",
"classic SELECT->LOADING 已在阶段回调内按 40250 SetLoadingPhase 清旧 world,并以空 world guard 避免 GC_WARP 重复 resetNetPlay/NetWorld/特效的完整 owner 顺序和真实 server 包序仍待证明",
"_goto_game 等待模型时已移除主动 net_poll,仍需用运行时 fixture 证明模型构建期间不会发生重复 phase 或 entered_game",
"close_requested 已由 AppFlow 和旧 login.gd 分别消费并执行断开/解绑/退出;仍未证明真实 socket close 回调、重复 close、Windows 退出确认和最终 manager 销毁顺序",
"focus/后台音频、全屏、socket 暂停语义与 40250 仍不等价",
"client_main.gd -> AppFlow 与旧 login.gd 是两套启动/生命周期拓扑;需要统一入口或分别证明阶段、关闭、音频和销毁语义一致",
"没有 CPythonApplication::Destroy 对应的可观测总清理顺序和重复销毁测试"
],
"notes": "本轮完成启动、阶段、Loading、DEAD/CLOSE、焦点/后台、旧 login.gd 入口和销毁调用链审计;按 40250 修正 DeathUI 的 PHASE_DEAD 错误依赖、classic Loading 旧 world 清理、两条入口的 close_requested 消费和幂等退出,以及 _goto_game 重复夺取网络 pump。完整清理 owner、CLOSE 状态语义、跨节点阶段时序、旧入口和销毁顺序仍使合同保持 PARTIAL。",
"fingerprints": {
"reference": "7538cd86aa2937e6545ddf2d16bd75eba40cefcd4c9a2aa87b8e367906bfb3a5",
"implementation": "0845a372bd9f71cf3b2537dd537b85ff7c18c6af6a0efe81c67c0c646e1b15ca",
"tests": "e865f9782600d3d2027db84beb10913a22a22c751938b707768e7c838d894a1a"
}
},
{
"id": "network.character_select.create_delete",
"title": "角色列表、创建、删除、改名、选人",
"subsystem": "network",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/Packet.h",
"UserInterface/PythonNetworkStream.cpp",
"UserInterface/PythonNetworkStreamPhaseLogin.cpp",
"UserInterface/PythonNetworkStreamPhaseSelect.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp"
],
"symbols": [
"CPythonNetworkStream::SetLoginPhase",
"CPythonNetworkStream::__RecvLoginSuccessPacket3",
"CPythonNetworkStream::__RecvLoginSuccessPacket4",
"CPythonNetworkStream::SetSelectPhase",
"CPythonNetworkStream::SendSelectEmpirePacket",
"CPythonNetworkStream::SendSelectCharacterPacket",
"CPythonNetworkStream::SendCreateCharacterPacket",
"CPythonNetworkStream::SendDestroyCharacterPacket",
"CPythonNetworkStream::SendChangeNamePacket",
"CPythonNetworkStream::__RecvPlayerCreateSuccessPacket",
"CPythonNetworkStream::__RecvPlayerCreateFailurePacket",
"CPythonNetworkStream::__RecvPlayerDestroySuccessPacket",
"CPythonNetworkStream::__RecvPlayerDestroyFailurePacket",
"CPythonNetworkStream::__RecvChangeName",
"CPythonNetworkStream::ConnectGameServer",
"CPythonNetworkStream::__DirectEnterMode_Set",
"CPythonNetworkStream::__DirectEnterMode_Initialize"
]
},
"implementation": {
"files": [
"extension/src/net/classic/wire_classic.h",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/m2_client.cpp",
"project/app_flow.gd",
"project/ui/char_select_screen.gd",
"project/intro_create_phase.gd"
],
"symbols": [
"ClassicParser::parse_login_success",
"ClassicParser::on_gc",
"ClassicSession::on_phase",
"ClassicSession::select_char",
"ClassicSession::create_character",
"ClassicSession::delete_character",
"ClassicSession::change_name",
"ClassicSession::send_empire",
"ClassicSession::connect_direct_enter",
"M2Client::select_character",
"M2Client::enter_game",
"M2Client::pump_classic",
"M2Client::build_char_list",
"AppFlow::_on_char_list",
"AppFlow::_build_char_list",
"CharSelectScreen::_pad_slots",
"CharSelectScreen::_do_start"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "MAPPED",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
{
"reference": "CPythonNetworkStream 固定槽位数组、phase/window callback",
"implementation": "ClassicParser vector、M2Client signal、Godot 选人场景",
"invariant": "数组/信号的适配必须保留固定槽位、包处理顺序、成功/失败分支和清理副作用;当前固定 3/4 槽与异常分支尚无证明",
"tests": [
"extension/tests/net_classic_session_test.cpp",
"extension/tests/net_loopback_test.cpp",
"project/char_create_delete_test.gd"
]
}
],
"evidence": {
"contract": "audit/contracts/network.character-select.md",
"tests": [
"project/char_create_delete_test.gd",
"project/test_intro_select_parity.gd",
"extension/tests/net_classic_session_test.cpp",
"extension/tests/net_loopback_test.cpp"
],
"last_test_result": "PASS"
},
"remaining": [
"中文角色名/改名字段已接入 40250 CP936/GB2312-compatible byte cap;仍需补齐其它 locale codepage 的真实 fixture",
"补齐创建/删除异常包、创建窗口与选人窗口双回调及连续包顺序测试",
"增加 DirectEnter 重复进入、连接失败和中断清理测试,并用真实服务端证明包序",
"补齐角色实例管理器的主实例、重复 VID、fade/dead/device/motion 生命周期",
"补齐角色 CRUD 之外的 Login/Select/Loading/Game 阶段 allowlist 与错误阶段副作用矩阵(由 network.packet_dispatch_protocol 继续跟踪)"
],
"notes": "按 40250 固定槽位、帝国写入时点、未知改名 PID、DirectEnter SelectPhase 分支、角色 CRUD 阶段门禁和中文 GB2312 fixed-name bytes 修复 classic 实现;native 角色/网络回归、角色 UI、选人和生命周期回归通过,非中文 locale、异常双窗口、重复进入、角色实例管理器和真实包序仍使合同保持 PARTIAL。",
"fingerprints": {
"reference": "19428229f676c622ba55bf9b59d788730fc0751b9cf10ecf3006fe5b88df66e4",
"implementation": "9b8136a0062b3268f0bda3d9f13e52e3a6a5aa2d105c22fb16cd38afb56e5c18",
"tests": "f56863d12c0cb2b8dcf535c2db8cab9b432d2dadd22f2bf133f0ec0630d7dafd"
}
},
{
"id": "network.packet_dispatch_protocol",
"title": "包头、分帧、阶段分派、未知包和发送顺序",
"subsystem": "network",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/Packet.h",
"UserInterface/PythonNetworkStream.cpp",
"EterLib/NetPacketHeaderMap.cpp",
"EterLib/NetStream.cpp"
],
"symbols": [
"CMainPacketHeaderMap::CMainPacketHeaderMap",
"CPythonNetworkStream::CheckPacket",
"CPythonNetworkStream::RecvErrorPacket",
"CPythonNetworkStream::RecvPhasePacket",
"CPythonNetworkStream::RecvPingPacket",
"CPythonNetworkStream::OnProcess",
"CPythonNetworkStream::LoginPhase",
"CPythonNetworkStream::SelectPhase",
"CPythonNetworkStream::LoadingPhase",
"CPythonNetworkStream::GamePhase",
"CNetworkPacketHeaderMap::Get"
]
},
"implementation": {
"files": [
"extension/src/net/classic/classic_stream.cpp",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/wire_classic.h",
"extension/src/net/net_stream.cpp"
],
"symbols": [
"ClassicStream::process",
"ClassicStream::feed",
"ClassicStream::dispatch",
"ClassicStream::handle_control",
"ClassicParser::on_gc",
"packet_size_gc",
"is_dynamic_gc",
"is_sequence_cg"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "MAPPED",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
"40250 Win32 CNetworkStream/phase tick 对应当前 Godot/ClassicStream 的 socket pump;平台替换不改变包头、分帧、allowlist 和失败清理语义。"
],
"evidence": {
"contract": "audit/contracts/network.packet-dispatch.md",
"tests": [
"extension/tests/net_classic_stream_test.cpp",
"extension/tests/net_classic_wire_test.cpp",
"extension/tests/net_classic_session_test.cpp",
".agents/skills/metin2-40250-parity-audit/scripts/audit_packet_registry.py",
".agents/skills/metin2-40250-parity-audit/scripts/audit_phase_dispatch.py",
".agents/skills/metin2-40250-parity-audit/scripts/audit_sequence_table.py",
".agents/skills/metin2-40250-parity-audit/scripts/audit_source_coverage.py"
],
"last_test_result": "PASS"
},
"remaining": [
"已按实际非 GAIDEN 条件生成 117 项 header 数值/静态动态类别并与当前 wire 表对齐(117/117);继续补齐每项 sizeof/byte layout 证据",
"明确所有协议别名(LOGIN_SUCCESS、CHARACTER_CREATE、ITEM_DEL 等)的 byte layout 和尺寸证据",
"继续用真实包序列证明 QUEST_INFO/GUILD 特殊重算与 40250 声明尺寸消费的差异不会错位;动态 handler 截断失败语义已收口",
"阶段 payload allowlist 已由 audit_phase_dispatch.py 按数值覆盖;继续补每个 Loading/Game handler 的副作用/拒绝清理矩阵",
"验证 GamePhase 在 8192 字节以上缓冲时的继续消费、精确 tick 驱动和完整 burst 事件矩阵",
"验证单包 tick 与 burst dispatch 的事件顺序,覆盖 leading zero、body zero、unknown、handler false、underflow、oversize、分片和连续包"
],
"notes": "已按 40250 固定头+文本尾语义修复 GC_WHISPERClassicSession 已收口 Login/Select/DirectEnter/Loading/Game 的阶段门禁,且由 audit_phase_dispatch.py 证明四阶段 payload case 按数值一致;Loading 的 ITEM_SET2 通过 GamePhase fallbackHybridCrypt 由 ClassicStream 消费后返回,phase/握手/初始密钥协商后 returnKEY_AGREEMENT_COMPLETED 会解密并继续派发同一缓冲区的密文,错误阶段包在当前帧消费后清空剩余 bufferClassicStream 已加入 Game 阶段小 burst 的 MAX_RECV_COUNT 边界、已知 handler 失败后的 RecvErrorPacket 清缓冲/owner 保留、CheckPacket 前导零语义,并补齐旧版 85 字节主角色包。按实际非 GAIDEN 条件,主游戏注册表数值/类别已覆盖 117/117,移除 6 个未注册普通流兼容包头,guild-mark 的 SYMBOL_DATA 保留为独立适配;动态 parser 对 SCRIPT、QUEST_INFO、MESSENGER、SHOP、GUILD、DUNGEON、NPC_POSITION、LAND_LIST、CHAT 的截断字段改为失败并复用该清理路径。wire/stream/session/encstream/phase-matrix 回归通过;逐项 sizeof/byte layout、8192 字节阈值、大量 burst 和 QUEST_INFO/GUILD 特殊边界仍使合同保持 PARTIAL。",
"fingerprints": {
"reference": "70235b4c609e89c03f033249fcd53dd4e775f579f4fae8c9cc86cc1dad3a77d3",
"implementation": "0be8e78eb0a0f6da7e1af10a3aa77fa43e93915fefc1bd7fc60c117174a57404",
"tests": "b45313ce3f4ab491face17e762d6715ca8d2d802175eb28c37ab138be844ed46"
}
},
{
"id": "network.security_encoding",
"title": "握手、序列号、加密、编码和定长字符串",
"subsystem": "network",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonNetworkStreamPhaseHandShake.cpp",
"UserInterface/PythonNetworkStreamPhaseLogin.cpp",
"UserInterface/PythonNetworkStreamPhaseSelect.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/AccountConnector.cpp",
"EterLib/NetStream.cpp",
"EterBase/cipher.cpp",
"EterBase/cipher.h",
"EterBase/tea.cpp",
"EterLocale/StringCodec.cpp",
"UserInterface/Locale_inc.h"
],
"symbols": [
"CPythonNetworkStream::HandShakePhase",
"CPythonNetworkStream::RecvHandshakePacket",
"CPythonNetworkStream::RecvHandshakeOKPacket",
"CPythonNetworkStream::RecvHybridCryptKeyPacket",
"CPythonNetworkStream::RecvHybridCryptSDBPacket",
"CPythonNetworkStream::RecvKeyAgreementPacket",
"CPythonNetworkStream::RecvKeyAgreementCompletedPacket",
"CAccountConnector::__AuthState_RecvHandshake",
"CNetworkStream::SetSecurityMode",
"CNetworkStream::SendSequence",
"CNetworkStream::__SendInternalBuffer",
"CNetworkStream::Clear",
"CNetworkStream::ActivateCipher",
"Cipher::Prepare",
"Cipher::Activate",
"Ymir_WideCharToMultiByte",
"Ymir_MultiByteToWideChar"
]
},
"implementation": {
"files": [
"extension/src/net/classic/classic_stream.cpp",
"extension/src/net/classic/classic_stream.h",
"extension/src/net/classic/classic_cipher.cpp",
"extension/src/net/classic/classic_cipher.h",
"extension/src/net/classic/sequence_table.h",
"extension/src/net/classic/wire_classic.h",
"extension/src/net/text_codec.h",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/m2_client.cpp",
"extension/src/net/secure_cipher.cpp",
"extension/src/net/secure_cipher.h"
],
"symbols": [
"mtnet::classic::ClassicStream::handle_control",
"mtnet::classic::ClassicStream::server_frame_ms",
"mtnet::classic::ClassicStream::emit_bytes",
"mtnet::classic::ClassicStream::handle_key_agreement",
"mtnet::classic::ClassicStream::disconnect",
"mtnet::classic::ClassicCipher::prepare",
"mtnet::classic::ClassicCipher::activate",
"mtnet::classic::ClassicCipher::encrypt",
"mtnet::classic::ClassicCipher::decrypt",
"mtnet::classic::is_sequence_cg",
"mtnet::classic::SEQUENCE_TABLE",
"mtnet::to_wire",
"mtnet::from_wire_str",
"ClassicSession::send_client_version"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
"40250 依赖 Windows EterLocale/codepage、WinSock、EterPack 和可选 anti-cheat provider;当前是 POSIX/Godot port。",
"当前 classic 保留了 40250 DH2/CTR 的算法形状,但 UTF-8、steady_clock、Godot 资源链和 libsodium SecureCipher 不能视为 Windows classic 等价物。"
],
"evidence": {
"contract": "audit/contracts/network.security-encoding.md",
"tests": [
"extension/tests/net_classic_cipher_test.cpp",
"extension/tests/net_classic_encstream_test.cpp",
"extension/tests/net_text_codec_test.cpp",
"extension/tests/net_classic_session_test.cpp"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"补齐全局 ELTimer/CTimer server clock owner、auth/game 双 connector 和真实 wire fixture",
"补齐 40250 server polarity 的 DH2/CTR 分片、flush 时点、非法长度、重复 agreement、completed 和 reconnect fixture",
"实现或明确 legacy TEA、HybridCrypt keys/SDB、Panama pack IV 和 HS/XTrap 的兼容策略与清理",
"从 PacketInfo/CMainPacketHeaderMap 生成完整 bSeq 对照矩阵(sequence table 本体已逐字节校验)",
"中文 40250 固定字段已接入 CP936/GB2312-compatible conversion 并覆盖 NUL、24-byte cap、不可表示字符和 reverse decode;仍需补齐 CP949/CP1252/CP1258 等其它 locale codepage"
],
"notes": "已按 40250 handshake/handshake_ok 修复后续 server clock 二次校准,并通过 sequence table 32768 项逐字节核对;DH2/CTR fixture、全局 clock owner、legacy/optional security 分支、Windows codepage 和完整 bSeq 矩阵仍使合同保持 PARTIAL。",
"fingerprints": {
"reference": "40701de0c80d0226ca751b34ef81a6f96f2cb005f8012a5c1f7ed5148ec5e4d6",
"implementation": "b1a5d95818f5d0e4e21da62f9262cbd560f452ff42a9b0a2db5ac0e4120d5f18",
"tests": "f6a16e75e4a98ee53f2757dd3ecc05b715cc353ee98c40365e624f7e9386039d"
}
},
{
"id": "network.reconnect_warp",
"title": "断线、换图、跨服重连和旧会话清理",
"subsystem": "network",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamPhaseLoading.cpp",
"UserInterface/PythonNetworkStream.cpp",
"UserInterface/NetworkActorManager.cpp",
"EterLib/NetStream.cpp"
],
"symbols": [
"CPythonNetworkStream::RecvWarpPacket",
"CPythonNetworkStream::ConnectGameServer",
"CPythonNetworkStream::SetLoadingPhase",
"CPythonNetworkStream::__LeaveGamePhase",
"CNetworkStream::Connect",
"CNetworkStream::Clear"
]
},
"implementation": {
"files": [
"extension/src/net/m2_client.cpp",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/entity_store.cpp",
"project/game_scene.gd",
"project/net_play.gd",
"project/net_world.gd",
"project/app_flow.gd",
"project/ui/reconnect_ui.gd"
],
"symbols": [
"ClassicSession::connect_direct_enter",
"ClassicSession::connect_warp",
"ClassicSession::pump",
"M2Client::reconnect",
"M2Client::warp_to_game_server",
"M2Client::pump_classic",
"EntityStore::reset_for_map_change",
"AppFlow::_on_disconnected",
"AppFlow::_on_char_list",
"ReconnectUI::_retry_now"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "MAPPED",
"state_transition_order": "PARTIAL",
"constants_units": "MAPPED",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
"Win32 CNetworkStream::Connect/Clear 对应当前 ClassicStream disconnect/connect;平台替换不应改变 GC_WARP 的 selected slot、login key、DirectEnter 和 Loading 清理顺序。"
],
"evidence": {
"contract": "audit/contracts/network.reconnect-warp.md",
"tests": [
"extension/tests/net_classic_session_test.cpp",
"project/app_flow_lifecycle_test.gd",
"project/gamescene_test.gd",
"project/p9_test.gd",
"project/p10_test.gd"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"补齐真实 GC_WARP、同服/跨服、旧 socket 与新 socket 包序矩阵",
"验证 generic reconnect 保留 session 后 selected slot/direct-enter 的真实服务端包序,并覆盖无 ticket/无 slot fallback",
"对齐 Loading、Game leave、EntityStore、NetPlay、NetWorld、Ground/Fly/Effect/UI 的清理 owner 和时序",
"增加 connect deadline 实际超时、in-flight guard、重试上限/停止、重复 reconnect、重复 warp 和旧会话竞态测试"
],
"notes": "已按 40250 同一 CNetworkStream Clear->Connect 语义修复 classic generic reconnect 的 session/selected slot/login key 保留,加入 3000ms 连接 deadline、连接中幂等 guard,并修复多 warp cue 在第一个重连 return 后被整批清空的问题;显式 disconnect 现在先 reset 活动 EntityStore、发 world_reset,再释放 transport,并有独立回归。真实阻塞连接、旧新 socket 竞态、包序和清理 owner 时序仍使合同保持 PARTIAL。",
"fingerprints": {
"reference": "3797fce63b45cc47556f83c588699d6bf04000ff1bf821fa3b568b20006fac6c",
"implementation": "de467a517c36ed61f0fd0bb94a6f2df85644d57690c50e198ec6c9e5c4e8ddd0",
"tests": "9dfa17b7705966830888646381d7abe70653a2b381383144489c2f0d456899e5"
}
},
{
"id": "network.error_timeout_server_state",
"title": "登录错误、超时、频道/服务器状态和恢复",
"subsystem": "network",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/AccountConnector.cpp",
"UserInterface/ServerStateChecker.cpp",
"UserInterface/PythonNetworkStreamPhaseLogin.cpp"
],
"symbols": []
},
"implementation": {
"files": [
"extension/src/net/auth_client.h",
"project/net/channel_status.gd",
"project/net/serverinfo.gd",
"project/app_flow.gd"
],
"symbols": []
},
"equivalence": {},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/network.error-timeout-server-state.md",
"tests": [
"project/app_flow_lifecycle_test.gd",
"project/channel_status_test.gd",
"MT_PROTOCOL=classic godot --headless --path project --script channel_status_test.gd",
"extension/tests/net_loopback_test.cpp"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"补齐 auth failure、remote close 和 channel status 完整矩阵",
"证明 count 边界、fallback probe 和恢复顺序",
"增加重试上限、窗口销毁和 login/reconnect 竞态清理测试"
],
"notes": "本轮按 40250 修复认证密码生命周期、两套 transport 的连接 deadline/可写完成判定,以及 ChannelStatus 旧 probe 与重入 callback 清理;频道检测仍保留当前端逐频道 TCP fallback,认证失败/远端关闭/完整恢复与边界矩阵仍未闭合,合同保持 PARTIAL。",
"fingerprints": {
"reference": "cbfb26c28228d50ea6af675e69f7766d06a6292574bd913e5db5d56979bedd4e",
"implementation": "c203e25d94a95cf8c342692525a4f8a2bf8d5a1885f209b52e4e884be291bf4d",
"tests": "0977a59547cbe394ab546237bd1ababe4e3069d946487b91ede25ea3e4dc5778"
}
},
{
"id": "world.entity_spawn_state_sync",
"title": "Actor 创建、追加信息、VID 重用、状态同步",
"subsystem": "world",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/NetworkActorManager.cpp",
"UserInterface/PythonNetworkStreamPhaseGameActor.cpp",
"UserInterface/PythonCharacterManager.cpp",
"UserInterface/InstanceBase.cpp"
],
"symbols": [
"CNetworkActorManager::AppendActor",
"CNetworkActorManager::UpdateActor",
"CNetworkActorManager::MoveActor",
"CNetworkActorManager::RemoveActor",
"CNetworkActorManager::SetMainActorVID",
"CNetworkActorManager::__AppendCharacterManagerActor",
"CNetworkActorManager::__RemoveCharacterManagerActor",
"CPythonNetworkStream::RecvCharacterAppendPacket",
"CPythonNetworkStream::RecvCharacterAdditionalInfo",
"CPythonNetworkStream::RecvCharacterAppendPacketNew",
"CPythonNetworkStream::RecvCharacterUpdatePacket",
"CPythonNetworkStream::RecvCharacterDeletePacket",
"CPythonCharacterManager::DeleteInstance",
"CPythonCharacterManager::DeleteInstanceByFade",
"CInstanceBase::SCreateData"
]
},
"implementation": {
"files": [
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/classic_parser.h",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/m2_client.cpp",
"extension/src/register_types.cpp",
"project/net_world.gd",
"project/net_world_main_switch_test.gd",
"project/game_scene.gd",
"project/net_play.gd",
"project/ui/remote_player_view.gd",
"project/ui/mob_view.gd"
],
"symbols": [
"ClassicParser::handle_character_add",
"ClassicParser::handle_character_add2",
"ClassicParser::handle_character_additional_info",
"ClassicParser::handle_character_update",
"ClassicParser::handle_character_delete",
"EntityStore::apply",
"EntityStore::mut_spawn",
"EntityStore::mut_spawn_full",
"EntityStore::mut_char_info",
"EntityStore::mut_char_update",
"EntityStore::mut_affect_flags",
"EntityStore::mut_despawn",
"NetWorld::_on_spawn",
"NetWorld::_on_info",
"NetWorld::_on_despawn",
"NetWorld::_on_main_set",
"NetWorld::_fade_and_free",
"NetWorld::clear_for_map_change"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"data_completeness": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
"CInstanceBase/CPythonCharacterManager 映射为 EntityStore 数据层 + Godot NetWorld 节点层",
"DeleteInstanceByFade 映射为 Godot fade tween + queue_free",
"40250 manager update 映射为 EntityStore tick + Godot frame 同步"
],
"evidence": {
"contract": "audit/contracts/world.entity-spawn-state-sync.md",
"tests": [
"extension/tests/net_entity_test.cpp",
"extension/tests/net_classic_session_test.cpp",
"project/net_world_respawn_test.gd",
"project/net_world_vis_test.gd",
"project/net_world_main_switch_test.gd",
"project/gamescene_test.gd"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"补齐 Delete 后迟到 AdditionalInfo、map reset 后迟到包",
"证明主 VID 切换对远端 Actor 字典、动态实例和 UI 引用的清理范围",
"增加模型失败、升级中删除、重复 Delete、重复 Add 和跨帧 signal 自动测试"
],
"notes": "本轮补充了同 VID 普通重建/上下马位置保留、淡出期间同 VID 立即重建、单槽 pending、逐帧可见性、主角 MainSet 通知次数,以及 classic/generic Add/Add2/AdditionalInfo/Update 的完整字段、隐身过滤、unknown VID 和事件粒度回归;net_entity_test、net_classic_session_test、net_world_respawn_test 与 net_world_vis_test 通过,Delete/map reset 后迟到包、旧 dead-list/tween 跨帧并存、主 VID 清理和 Godot 渲染副作用顺序仍未闭合,当前不能视为 40250 等价。",
"fingerprints": {
"reference": "e59399f499670d966befae34478d84bb3ae4699da942c4707c1bc059c84c4788",
"implementation": "80a2daf3ea649b43f2e882068103c04774fdd4bec021fca67f6a1b0d15f85b93",
"tests": "720b1c6a90f62614e9867f51a9dd039b695c4141d2f8d568e9298637620a1779"
}
},
{
"id": "world.map_load_transition",
"title": "地图、区域、地形、加载阶段和场景重建",
"subsystem": "world",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"GameLib/MapOutdoor.cpp",
"UserInterface/PythonBackground.cpp",
"GameLib/MapManager.cpp",
"GameLib/MapOutdoorLoad.cpp",
"GameLib/MapOutdoorUpdate.cpp",
"GameLib/AreaLoaderThread.cpp"
],
"symbols": [
"CMapManager::LoadMap",
"CMapManager::UnloadMap",
"CMapManager::UpdateMap",
"CMapManager::Clear",
"CMapOutdoor::Load",
"CMapOutdoor::Destroy",
"CMapOutdoor::Clear",
"CMapOutdoor::LoadSetting",
"CMapOutdoor::LoadTerrain",
"CMapOutdoor::LoadArea",
"CMapOutdoor::Update",
"CMapOutdoor::AssignTerrainPtr",
"CMapOutdoor::UpdateAreaList",
"TEMP_CAreaLoaderThread::Request",
"TEMP_CAreaLoaderThread::Fetch",
"TEMP_CAreaLoaderThread::Shutdown"
]
},
"implementation": {
"files": [
"extension/src/metin2_world.cpp",
"extension/src/metin2_world.h",
"project/game_scene.gd",
"project/net_play.gd",
"project/net_world.gd",
"project/app_flow.gd",
"extension/src/environment_builder.cpp",
"project/ui/atlas_ui.gd"
],
"symbols": [
"Metin2World::load_map",
"Metin2World::unload_map",
"Metin2World::stream_update",
"Metin2World::set_focus_position",
"Metin2World::_process",
"Metin2World::sample_height",
"GameScene::setup",
"GameScene::_on_world_reset",
"GameScene::_on_warp",
"GameScene::_reload_map_for_warp",
"GameScene::_build_world",
"NetPlay::clear_for_map_change",
"NetWorld::clear_for_map_change"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "GAP",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "MAPPED",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
"CMapOutdoor terrain/area loader 映射为 Metin2World chunk + Godot Node3D",
"DirectX loader thread 映射为主线程 stream_queue + stream_budget",
"CMapOutdoor environment/sky 映射为 .msenv 解析 + Godot Sun/WorldEnvironment",
"原版延迟 delete vector 映射为 chunk root queue_free,需保留可观察卸载边界"
],
"evidence": {
"contract": "audit/contracts/world.map-load-transition.md",
"tests": [
"project/gamescene_test.gd",
"project/p9_test.gd",
"project/forest_map_render_test.gd",
"project/environment_test.gd"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"跨图 warp 的 Leave/Clear、旧 world 销毁与新 world Load/Enter 顺序",
"map-name unload 校验和 load failure 的 hard/fallback/retry 语义",
"缺失 environment、textureset、terrain/area/object 的失败与资源释放矩阵",
"重复 warp、加载中 reset、旧异步结果回灌和 generation/cancel 边界",
"streaming 边界与碰撞、height、water、对象、ambience、实体可见性的逐格测试",
"forest_map_render_test 的正确 runner/报告路径纳入可执行回归"
],
"notes": "本轮补充了 LoadMap/UnloadMap/UpdateMap 与当前 Metin2World 的 Leave/Enter、初始同步建队列、延迟回收、map-name 校验、双 world 和旧请求 generation 差异;gamescene/p9/environment 测试通过但不覆盖这些边界,合同保持 PARTIAL。",
"fingerprints": {
"reference": "e985648a82b4de1f2d28872b21ffc2b885622ee94f9eb0dc03aa1d6dfc4ff3b8",
"implementation": "7c308d9ca6c888fa85f23d8d3465a6f41c6d8caae1092c0fac72271a7b9a64d7",
"tests": "67a02ec9abd6ee1ead261c0e1353115e69b34934975dbb071b2faab05c436e57"
}
},
{
"id": "world.npc_drop_actor_spawn",
"title": "NPC、怪物、建筑、掉落物和招牌实体",
"subsystem": "world",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonNonPlayer.cpp",
"UserInterface/PythonNetworkStreamPhaseGameActor.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamPhaseGameItem.cpp",
"UserInterface/NetworkActorManager.cpp",
"UserInterface/PythonCharacterManager.cpp",
"UserInterface/InstanceBase.cpp",
"UserInterface/PythonItem.cpp",
"UserInterface/PythonTextTail.cpp",
"UserInterface/Packet.h"
],
"symbols": [
"CPythonNetworkStream::RecvCharacterAppendPacket",
"CPythonNetworkStream::RecvCharacterAdditionalInfo",
"CPythonNetworkStream::RecvCharacterUpdatePacket",
"CPythonNetworkStream::RecvCharacterDeletePacket",
"CPythonNetworkStream::RecvOwnerShipPacket",
"CPythonNetworkStream::RecvItemGroundAddPacket",
"CPythonNetworkStream::RecvItemGroundDelPacket",
"CPythonNetworkStream::RecvItemOwnership",
"CPythonNetworkStream::RecvShopSignPacket",
"CNetworkActorManager::AppendActor",
"CNetworkActorManager::RemoveActor",
"CPythonCharacterManager::__NEW_Pick",
"CPythonItem::CreateItem",
"CPythonItem::SetOwnership",
"CPythonItem::DeleteItem",
"CPythonTextTail::SetItemTextTailOwner"
]
},
"implementation": {
"files": [
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/wire_classic.h",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/m2_client.cpp",
"project/net_world.gd",
"project/ui/mob_view.gd",
"project/ui/ground_items.gd",
"project/entity_rules.gd",
"project/game_scene.gd",
"project/metin_spawn_event_system.gd",
"project/mob_drop_scatter.gd"
],
"symbols": [
"EntityStore::mut_spawn_full",
"EntityStore::mut_despawn",
"EntityStore::mut_ground_add",
"EntityStore::mut_ground_del",
"EntityStore::mut_ground_owner",
"EntityStore::mut_shop_sign",
"M2Client::poll",
"NetWorld::_on_spawn",
"NetWorld::_on_info",
"NetWorld::_update_visibility",
"NetWorld::_fade_and_free",
"NetWorld::_on_despawn",
"NetWorld::_refresh_shop_sign",
"GroundItems::_on_added",
"GroundItems::_on_removed",
"GroundItems::try_pickup",
"MobView::build",
"MobView::set_anim_state"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [
"CNetworkActorManager/CPythonCharacterManager 映射为 EntityStore + M2Client + NetWorld 两层数据/场景生命周期",
"CItemData drop model 与 CPythonTextTail 映射为 ground_items 的 Godot 模型、Label3D 和 owner_tag",
"DirectX actor blend-out 映射为 Godot Tween/queue_free,必须保持数据行与可见节点分离"
],
"evidence": {
"contract": "audit/contracts/world.npc-drop-actor-spawn.md",
"tests": [
"project/net_world_vis_test.gd",
"project/entity_rules_test.gd",
"project/forest_mob_render_test.gd",
"project/mob_view_test.gd",
"project/net_world_push_test.gd",
"project/test_ground_pickup_parity.gd",
"project/mob_death_drop_test.gd",
"project/test_mob_drop_scatter_parity.gd",
"project/mob_winding_test.gd",
"project/test_metin_spawn_event_parity.gd",
"build/extension/net_entity_test",
"build/extension/net_classic_session_test",
"build/extension/net_classic_wire_test"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"修复并回归 net_world_vis_test 的 4 项可见性/裁剪失败,确认节点只建一次、离开范围只删一次",
"补齐不可见 race、未知 race/vnum、模型工厂未就绪、真实 drop model/effect/sound 和建筑碰撞的失败矩阵",
"验证 ground add/owner/del 与 CG_ITEM_PICKUP→GC_DEL/ITEM_GET/ITEM_UPDATE 的 live 包序、失败、乱序和重连清理",
"验证 GC_SHOP_SIGN 空/非空、未知 VID、重复包、私人商店 UI 和换图/断线清理",
"把本地掉落散落、活动石头和 owner 保护与 40250 client presentation 分层,不把 custom authority 当 parity"
],
"notes": "已完成 40250 actor/ground/shop-sign 调用链和当前端 parser/store/view 对照;合同保持 PARTIAL。net_world_vis_test 复现 4 项可见性生命周期失败,本轮仅登记审计结果,未修改实现。",
"fingerprints": {
"reference": "3bf9532d6c9f258fbbc3dba6864171160594f94c7cd708e056c250f88be126ff",
"implementation": "3a7d87cad0ba405495ba51e13feab294af9ebddbde2fb18b5df9c606bbe3af08",
"tests": "9783566cc5ee2f17b89509082c9fd8d9334154f9b03fb499c1616613bba3a2ab"
}
},
{
"id": "world.reset_cleanup",
"title": "换图、死亡、退出和 UI/实体/特效清理",
"subsystem": "world",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonNetworkStream.cpp",
"UserInterface/PythonNetworkStreamPhaseLoading.cpp",
"UserInterface/PythonPlayer.cpp",
"UserInterface/PythonApplicationProcedure.cpp",
"UserInterface/PythonEventManager.cpp",
"GameLib/MapManager.cpp",
"UserInterface/NetworkActorManager.cpp",
"UserInterface/FlyingObjectManager.cpp",
"EterLib/EffectManager.cpp"
],
"symbols": [
"CPythonNetworkStream::SetLoadingPhase",
"CPythonNetworkStream::RecvWarpPacket",
"CPythonNetworkStream::__DirectEnterMode_Initialize",
"CPythonPlayer::Clear",
"CFlyingManager::DeleteAllInstances",
"CEffectManager::DeleteAllInstances",
"CNetworkActorManager::Destroy",
"CNetworkActorManager::__RemoveAllActors",
"CNetworkActorManager::__RemoveAllGroundItems",
"CMapManager::UnloadMap",
"CPythonApplication::Destroy"
]
},
"implementation": {
"files": [
"project/app_flow.gd",
"project/game_scene.gd",
"project/audio.gd",
"project/net_play.gd",
"extension/src/net/wire.h",
"extension/src/net/entity_store.cpp",
"extension/src/net/m2_client.cpp",
"project/net_world.gd",
"project/fly_object.gd",
"project/ground_items.gd",
"project/bgm_director.gd"
],
"symbols": [
"M2Client::pump_classic",
"M2Client::disconnect_from_server",
"EntityStore::reset_for_map_change",
"GameScene::_on_world_reset",
"GameScene::_reload_map_for_warp",
"GameScene::_exit_tree",
"AppFlow::_exit_tree",
"Audio::shutdown",
"NetPlay::clear_for_map_change",
"NetWorld::clear_for_map_change",
"FlyObject::clear_for_map_change",
"Metin2World::unload_map",
"uninitialize_mtgodot_module"
]
},
"equivalence": {
"preconditions": "MAPPED",
"branch_structure": "PARTIAL",
"algorithms_formulas": "MAPPED",
"state_transition_order": "PARTIAL",
"constants_units": "MAPPED",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
"CPythonPlayer/Actor/Flying/Effect manager 映射为 EntityStore + NetPlay + NetWorld + FlyObject + Godot effect registry",
"同步删除映射为信号同步清理与 queue_free 延迟释放",
"CPythonApplication Destroy 映射为 AppFlow/GameScene/RAII 生命周期"
],
"evidence": {
"contract": "audit/contracts/world.reset-cleanup.md",
"tests": [
"extension/tests/net_entity_test.cpp",
"project/fly_reset_test.gd",
"project/screen_hp_bar_test.gd",
"project/net_world_reset_test.gd",
"project/app_flow_lifecycle_test.gd",
"project/gamescene_test.gd",
"project/rendering_scenario_test.gd",
"project/disconnect_world_reset_test.gd"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"GC_WARP/重连/close/dead 的完整顺序矩阵",
"旧地图先清理还是新地图先构造的资源与引用安全证明",
"统一 generation 防止 queue_free/模型升级/旧网络回调回灌",
"真实断线重连保留 GameScene 时的重复 world_reset/旧数据清理包序",
"通用特效 manager、窗口关闭和显式 Destroy 等价测试",
"清理 test_exp_fly_parity 旧式单函数 fixture 的 3 个 ObjectDB 泄漏与 SceneTree path warning"
],
"notes": "本轮按 SetLoadingPhase、CMapManager::UnloadMap、CSoundManager::Destroy 和 CPythonApplication::Destroy 的清理边界,新增 net_world_reset_test,修复 _upgrade_queue、_chat_tails、target/hover VID、target effect 残留,并将 GameScene 换图改为旧 world 先 unload 再创建新 worldAppFlow、GameScene、Audio 与 GDExtension 退出路径清空模型/动作/NPC/UI/音频缓存,rendering_scenario 的 139/54 泄漏和 Debug 下 2 个 AudioStream 泄漏已修复。FlyInstance handler 自捕获环也已解除,fly_reset_test 的 5 个 ObjectDB 泄漏已清零;显式 disconnect 现在先 reset 活动 EntityStore、发 world_reset 再释放 transporttest_exp_fly_parity 仍有 3 个旧 fixture 警告。真实重连包序、统一 generation 和通用 EffectManager 仍使合同保持 PARTIAL。",
"fingerprints": {
"reference": "30cb04eed7ba1b82165b30583723a2aaf89f98d1a7a2e7e17b327bb16be10605",
"implementation": "260e6182daa7f44706bebb081eea849b0d4d58c57e5cea129c2920d715c47743",
"tests": "d3cc153e1d36bb4f390b88efc623e3cd56d1703626d7b9f325f9ca3c457adf1d"
}
},
{
"id": "movement.click_collision",
"title": "点地、路径目标、地形/Actor 碰撞和阻挡恢复",
"subsystem": "movement",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonPlayerInputMouse.cpp",
"UserInterface/PythonPlayerInputKeyboard.cpp",
"UserInterface/InstanceBaseMovement.cpp",
"GameLib/ActorInstanceCollisionDetection.cpp",
"GameLib/MapOutdoor.cpp",
"GameLib/GameUtil.cpp"
],
"symbols": [
"CPythonPlayer::NEW_SetMouseState",
"CPythonPlayer::NEW_SetMouseSmartState",
"CPythonPlayer::__OnPressSmart",
"CPythonPlayer::__OnClickSmart",
"CPythonPlayer::NEW_RefreshMouseWalkingDirection",
"CPythonPlayer::__OnPressGround",
"CPythonPlayer::__OnClickGround",
"CInstanceBase::NEW_CanMoveToDestPixelPosition",
"CInstanceBase::NEW_MoveToDestPixelPositionDirection",
"CInstanceBase::NEW_MoveToDirection",
"CInstanceBase::NEW_Stop",
"CActorInstance::TestActorCollision",
"CActorInstance::AvoidObject",
"CActorInstance::IsBlockObject",
"CActorInstance::BlockMovement",
"CMapOutdoor::GetPickingPointWithRay",
"CMapOutdoor::GetHeight",
"CMapOutdoor::isAttrOn"
]
},
"implementation": {
"files": [
"project/player_controller.gd",
"project/ui/mouse_controller.gd",
"project/hit_collision.gd",
"extension/src/metin2_world.cpp",
"project/game_scene.gd",
"project/net_play.gd",
"project/ui/ground_items.gd"
],
"symbols": [
"PlayerController::_unhandled_input",
"PlayerController::_on_click",
"PlayerController::_goto",
"PlayerController::_ray_ground",
"PlayerController::_get_blocking_actor",
"PlayerController::_blocked",
"PlayerController::_process",
"NetPlay::pick_ground_item",
"NetPlay::_process_reserved_action",
"GameScene::_on_ground_clicked",
"Metin2World::sample_height",
"Metin2World::is_blocked"
]
},
"equivalence": {
"preconditions": "MAPPED",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "MAPPED",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [
"DirectX camera ray/object picking 映射为 Godot project_ray + mesh AABB/ground ray",
"CActorInstance sphere collision 映射为 PlayerController 半径/切线/分轴近似 + HitCollision effect geometry",
"CPythonPlayer reservation loop 映射为 GDScript _process 预约队列"
],
"evidence": {
"contract": "audit/contracts/movement.click-collision.md",
"tests": [
"project/player_move_test.gd",
"project/movement_parity_test.gd",
"project/netplay_test.gd",
"project/test_click_target_effect_parity.gd"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"实现/证明 MBS_PRESS 与 MBS_CLICK 的 Smart/Auto/Camera/Attack/Skill 完整状态矩阵",
"运行时 movable distance 与 item/actor reservation 的完整包序列",
"MSM 骨骼碰撞球、坐骑/攻击状态选择、第二个碰撞停止和移动段采样",
"对象 Avoid/BlockMovement 与当前 direct/切线/xz 分轴推进的终止语义",
"桥/台阶 object picking、terrain height 和 ATTRIBUTE_BLOCK 的射线边界",
"实体删除、失焦、死亡、换图中断、连续点击和退出资源泄漏测试"
],
"notes": "本轮补充核对了 Smart press/hold/release、click miss 的停止语义、运行时距离门、真实 MSM 碰撞球、第二碰撞 BlockMovement 和对象 picking 的差异;四组移动/点选回归均通过,但 click/target/fly 测试有 3 个 ObjectDB 泄漏和 1 个资源退出警告。当前仍不能用固定半径替代真实 MSM sphere,合同保持 PARTIAL。",
"fingerprints": {
"reference": "15850029efadc3a50e52511fb45f51da5ad77e6ce0841a41a53d66fd6306d03d",
"implementation": "d2e8fc11d36e5f898a4aaea356ce4bee03d1d43b7dea7101c5c610a74adefd76",
"tests": "cdee05a904f96b260c201553915af522bd3623eed78ffb5b23d16ae8e0b2a88a"
}
},
{
"id": "movement.remote_sync_state",
"title": "远端移动命令队列、服务器时钟和状态释放",
"subsystem": "movement",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/InstanceBase.cpp",
"UserInterface/NetworkActorManager.cpp",
"UserInterface/PythonNetworkStreamPhaseGameActor.cpp",
"UserInterface/PythonNetworkStreamPhaseHandShake.cpp",
"GameLib/ActorInstance.cpp",
"GameLib/ActorInstanceSync.cpp"
],
"symbols": [
"PushTCPState",
"StateProcess",
"MovementProcess",
"TEMP_Push",
"RecvCharacterMovePacket",
"RecvSyncPositionPacket"
]
},
"implementation": {
"files": [
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"project/net_play.gd",
"project/net_world.gd",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/m2_client.cpp",
"extension/src/net/m2_client.h",
"extension/tests/net_state_queue_test.cpp",
"extension/tests/net_entity_test.cpp",
"extension/tests/net_bounds_test.cpp",
"project/net_world_remote_action_test.gd"
],
"symbols": [
"EntityStore::mut_move",
"EntityStore::drain_state_queue",
"EntityStore::apply_state_cmd",
"EntityStore::tick",
"EntityStore::mut_snap_position",
"Metin2World::_update_visibility",
"M2Client::set_entity_knockdown",
"NetWorld::_apply_sync_push",
"NetWorld::_sync_push_collision",
"NetWorld::_sync_pushed_position"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/movement.remote-sync-state.md",
"tests": [
"extension/tests/net_state_queue_test.cpp",
"extension/tests/net_entity_test.cpp",
"project/netplay_test.gd",
"project/movement_parity_test.gd"
],
"last_test_result": "PASS"
},
"remaining": [
"FUNC_EMOTION 双人目标 VID 从 GC_MOTION/GC_MOVE 的完整资源路径",
"技能可取消/grade 的 40250 动作数据权威来源和真实尾事件",
"真实地图 IPhysicsWorld/模型资源下 TEMP_Push 与客户端碰撞结果的逐点验证",
"远端怪物 combo/skill arg 到具体动作资源的全量映射",
"真实 40250 服务器握手时基、乱序/跨图/死亡重建端到端验证"
],
"notes": "主队列和时钟链已逐分支审计;远端 FUNC_MOVE 已按 40250 统一 PC/NPC/monster 的穿越 Dst 分支,TEMP_Push 已补 correction serial、100 点碰撞探测、1 秒表现缓动和大位移击倒门。因技能数据权威来源、双人情绪目标、真实资源/地图碰撞和服务器时序差异保持 PARTIAL。",
"fingerprints": {
"reference": "2dc90fd41d26f4a61085fdd57d961ffd8c83f5b1ef4d359559c3104dcbabe801",
"implementation": "99d49c732d9d141818db12ef3b2f98f081980fac372d34692fdb50ff775d9dca",
"tests": "f3fdfa1a6d0ebed1c8fb78588e44d92fb79ff6b1059d77453d9e7cd5f962e15c"
}
},
{
"id": "movement.motion_resource_events",
"title": "`.msa`/`.msm` 动作、连击窗口、动作事件和速度",
"subsystem": "movement",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/InstanceBaseMotion.cpp",
"GameLib/ActorInstanceMotion.cpp",
"GameLib/ActorInstanceMotionEvent.cpp",
"GameLib/RaceMotionData.cpp",
"GameLib/RaceData.cpp",
"GameLib/RaceData.h",
"GameLib/RaceManager.cpp",
"UserInterface/InstanceBaseBattle.cpp"
],
"symbols": [
"CRaceData::GetMotionKey",
"CRaceData::NEW_RegisterMotion",
"CRaceData::GetComboDataPointer",
"CRaceMotionData::LoadMotionData",
"CRaceMotionData::GetMotionDuration",
"CRaceMotionData::GetLoopStartTime",
"CRaceMotionData::GetLoopEndTime",
"CActorInstance::SetLoopMotion",
"CActorInstance::InterceptMotion",
"CActorInstance::PushOnceMotion",
"CActorInstance::PushLoopMotion",
"CActorInstance::ReservingMotionProcess",
"CActorInstance::CurrentMotionProcess",
"CActorInstance::__MotionEventProcess",
"CActorInstance::MotionEventProcess",
"CActorInstance::ProcessMotionEventEffectEvent",
"CActorInstance::ProcessMotionEventEffectToTargetEvent",
"CActorInstance::ProcessMotionEventSpecialAttacking",
"CActorInstance::ProcessMotionEventSound",
"CActorInstance::ProcessMotionEventFly"
]
},
"implementation": {
"files": [
"project/ui/player_view.gd",
"project/ui/motion_registry.gd",
"project/ui/hit_reaction.gd",
"project/ui/remote_player_view.gd",
"project/ui/mob_view.gd",
"project/ui/msa_motion.gd",
"formats/msa.cpp",
"formats/msa.h",
"formats/msm.cpp",
"formats/msm.h",
"extension/src/metin2_anim.cpp",
"extension/src/metin2_anim.h",
"project/net_play.gd",
"project/game_scene.gd",
"project/fx/motion_effect_anchor.gd",
"project/hit_collision.gd"
],
"symbols": [
"Metin2AnimPlayer::reload",
"Metin2AnimPlayer::dispatch_events",
"Metin2AnimPlayer::get_motion_data",
"Metin2AnimPlayer::get_loop_data",
"Metin2AnimPlayer::get_effect_bone_pose",
"PlayerView::set_motion_mode",
"PlayerView::play_attack_motion",
"PlayerView::play_skill_motion",
"PlayerView::set_anim_state",
"PlayerView::get_move_motion_speeds",
"MobView::resolve_motion",
"MobView::_load_motlist",
"NetPlay::_combo_process",
"NetPlay::_begin_hit_windows",
"NetPlay::_attack_process",
"GameScene::_on_local_motion_event_detailed",
"GameScene::_apply_motion_warp"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [
"Granny/CGraphicThingInstance/CActorInstance motion queue 映射为 Metin2AnimPlayer + PlayerView/MobView/HitReaction",
"RaceData motion key 与 motlist/MSA 映射为 PC 硬编码 mode directories、NPC motlist 和 combo table parser",
"DirectX motion event/effect/sound dispatch 映射为 Godot signal + GameScene 本地事件分派"
],
"evidence": {
"contract": "audit/contracts/movement.motion-resource-events.md",
"tests": [
"project/player_motion_test.gd",
"project/keyboard_motion_timeline_test.gd",
"project/race_motion_assembly_test.gd",
"project/animation_blend_test.gd",
"project/animation_cache_test.gd",
"project/test_combo_continuous_flow.gd",
"project/combat_fx_test.gd",
"project/eterngrn_polish_test.gd",
"project/target_effect_test.gd",
"project/hit_collision_test.gd",
"project/motion_effect_anchor_test.gd",
"project/mob_view_test.gd",
"project/motion_event_payload_test.gd",
"project/motion_registry_test.gd",
"project/hit_view_test.gd"
],
"last_test_result": "PASS: PC playersettingmodule motion registry (registration order/weights), HORSE parent fallback, knockdown/stand-up early-return, MSA MotionDuration authority, LoopData skill timing, motion event payloads, and hot clip/MSA cache reuse regressions passed; NPC/full RaceData/event-consumer parity remains PARTIAL"
},
"remaining": [
"扩展已接入的 PC playersettingmodule registry 快照,对比 NPC motlist、horse/wedding/skill 的每个 motion key 和缺失 key 失败语义",
"让 MSA MotionDuration、LoopData count/start/end、60 FPS event frame 和 GR2 playback duration 形成固定样本契约",
"补齐非主角 Mob/remote PC 的 MotionEvent effect/target/fly/sound 消费与每 actor 清理;WARP 的目标前 270cm/阻挡/朝向分支已接入",
"验证同动作重入、动作队列、受击/死亡/晕倒/站起/技能取消、跨图和断线时序",
"处理事件测试 fixture 的 ObjectDB/RID 泄漏,并继续验证大 delta、LoopData 事件边界和动作队列"
],
"notes": "已完成 40250 RaceData/RaceMotionData/ActorInstanceMotion/ActorInstanceMotionEvent 与当前 MSA/MSM/AnimPlayer/视图/NetPlay 对照;本轮接入 playersettingmodule 驱动的 PC motion registry、复刻注册权重和 HORSE 父模式查找,并修正击倒/起身受击早退;MSA MotionDuration、LoopData 与热切换缓存回归仍通过,合同保持 PARTIAL。",
"fingerprints": {
"reference": "f59276d75e5cdf0fc54c95fdce5ca85c794080b94ba7fdee540870273004a471",
"implementation": "01ca0b13b2db5489c1fa37dc48d555d68da096dc2d2370d9d64b2cf78c8f9fc7",
"tests": "5815d9275c50109a98b6f21175c4ab274a0eb7cd4b4849dcf4b973a0b4c395d6"
}
},
{
"id": "combat.attack_combo_damage",
"title": "普攻、连击、命中窗口、伤害和受击",
"subsystem": "combat",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonPlayer.cpp",
"UserInterface/InstanceBaseBattle.cpp",
"GameLib/ActorInstanceBattle.cpp",
"GameLib/ActorInstanceWeaponTrace.cpp"
],
"symbols": []
},
"implementation": {
"files": [
"project/net_play.gd",
"project/player_controller.gd",
"project/hit_collision.gd",
"project/damage_effect.gd",
"project/ui/player_view.gd",
"project/ui/equip_model.gd",
"project/ui/weapon_trace.gd",
"project/socket_glow_fx_system.gd"
],
"symbols": []
},
"equivalence": {},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/combat.attack-combo-damage.md",
"tests": [
"project/combat_fx_test.gd",
"project/combat_parity_test.gd",
"project/gamescene_test.gd",
"project/weapon_trace_test.gd",
"project/weapon_trace_multi_part_parity_test.gd",
"project/weapon_trace_endpoint_parity_test.gd",
"project/weapon_trace_reach_affect_parity_test.gd",
"project/weapon_reach_scale_attack_parity_test.gd",
"project/damage_local_main_parity_test.gd",
"extension/tests/net_state_queue_test.cpp"
],
"last_test_result": "PARTIAL; local damage, multi-part WeaponTrace, bound-box/local-Z/reach-scale and attack-sweep regressions pass"
},
"remaining": [
"鼠标 press/release、空格自由攻击、弓 FLY 帧和自动攻击完整状态矩阵",
"服务端伤害/死亡/重复命中与客户端命中窗的真实包级 fixture",
"换图、断线、缺失动作资源和攻击队列清理测试",
"WeaponTrace 已按 bound-box 最大长度、局部 Z 和 reach scale 接入 PlayerView;当前仍未证明 native bone matrix 的同一 composite/rotation 坐标语义、远端/怪物覆盖、真实刀光纹理视觉和服务端动作资源",
"socket_glow_fx_system.has_trail 目前只有配置字段,没有进入武器刀光渲染链"
],
"notes": "已按 40250 isValidAttacking/HitDataContainer 删除缺失动作攻击数据、空命中窗和缺少 .msm 防御球时的本地合成命中;状态包、命中窗、去重、受击、弓箭 FLY、本地主角伤害飘字和 WeaponTrace 多部件生命周期有回归证据。WeaponTrace 目前通过 weapon_trace.gd 适配层接入,包含武器类型过滤、每个 Weapon/Shield 部件独立 trace、双轨迹时间队列、cubic spline triangle strip、alpha/texture 材质、攻击动作开关、纹理控制入口、bound-box 最大长度/局部 Z/reach scale 和换装清理;AFFECT_GEOMGYEONG 的 1.5/1.0 reach scale 已同时接入刀光与普通攻击扫掠。native 骨骼矩阵坐标语义、远端/怪物覆盖、真实视觉资源及服务端伤害闭环仍不等价。强化配置中的 has_trail 仍是独立未消费字段。",
"fingerprints": {
"reference": "3b79deef2e50cb16c6b7226e8900e4d96c82f5dbe4e409f40609b6a656586608",
"implementation": "fa633ddfb631df0d1b472ad674d558869399079d9789b54b6c6775e45b8310ac",
"tests": "228e1bb5ebb0ef52822956e3d33d9c51d55cc2090c323300b466f937249e5d3c"
}
},
{
"id": "combat.target_selection_rules",
"title": "目标选择、可攻击判定、目标框和安全区",
"subsystem": "combat",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonPlayerInputMouse.cpp",
"UserInterface/PythonPlayerInput.cpp",
"UserInterface/PythonPlayer.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/InstanceBase.cpp",
"UserInterface/InstanceBaseBattle.cpp",
"UserInterface/Packet.h"
],
"symbols": [
"CPythonPlayer::SetTarget",
"CPythonPlayer::__ChangeTargetToPickedInstance",
"CPythonPlayer::__OnClickActor",
"CPythonPlayer::__OnPressActor",
"CPythonPlayer::__Update_AutoAttack",
"CPythonPlayer::NotifyCharacterDead",
"CPythonPlayer::NotifyCharacterUpdate",
"CInstanceBase::IsAttackableInstance",
"CInstanceBase::IsTargetableInstance",
"CInstanceBase::CanPickInstance",
"CInstanceBase::CanViewTargetHP",
"CInstanceBase::IsInSafe",
"CPythonNetworkStream::RecvTargetPacket"
]
},
"implementation": {
"files": [
"project/entity_rules.gd",
"project/target_board.gd",
"project/ui/target_ui.gd",
"project/ui/mouse_controller.gd",
"project/player_controller.gd",
"project/net_play.gd",
"project/net_world.gd",
"project/alignment_pk_system.gd"
],
"symbols": [
"EntityRules.is_attackable_instance",
"EntityRules.can_pick_instance",
"NetPlay._is_attackable",
"NetPlay._attack_ctx",
"NetPlay._on_pick",
"NetPlay._change_target_to_picked_instance",
"NetPlay._motion_blocks_target_change",
"NetPlay._can_change_target",
"NetPlay._is_targetable_instance",
"NetPlay._reserve_process_click_actor",
"NetPlay._on_target_info",
"TargetBoard.classify",
"TargetBoard.can_view_target_hp",
"PlayerController._on_click",
"NetWorld._apply_field_updates",
"NetWorld.set_target_vid",
"NetWorld._on_despawn"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/combat.target-selection-rules.md",
"tests": [
"project/entity_rules_test.gd",
"project/target_board_test.gd",
"project/test_alignment_pk_parity.gd",
"project/test_click_target_effect_parity.gd",
"project/test_auto_combat_bot_parity.gd",
"project/playable_combat_test.gd",
"project/netplay_test.gd"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"完整 .msa 事件源下的 CanChangeTarget 动作时序和缺失动作资源回退验证",
"隐身、复活隐身、视锥、门死亡、裁剪/淡出和迟到实体更新的目标生命周期矩阵",
"地图 ATTRIBUTE_BANPK 与实体 in_safe 的实时数据权威和移动/换图更新链",
"SetTarget 切换时间窗、OnTargeted/OnUntargeted、飞行目标和 CG_TARGET 顺序",
"真实服务端目标包、死亡/删除/乱序/断线重连和目标框清理"
],
"notes": "已完成目标攻击资格、目标可选中、目标框和安全区的逐分支静态审计;目标入口已统一接入 CanChangeTarget/CanPickInstance、重复目标窗口和 CG_TARGET(0) 清理,合同保持 PARTIAL。",
"fingerprints": {
"reference": "818e201fffe6335de4b2c79b505d8749b7ef00d61267865861a8fefc21c5bdf5",
"implementation": "07fcaf36da0b7322ac63c5d2b3ec1001b6fff2c1810dd637c54b3fecfba430fb",
"tests": "9ad10ac749fbc12f4537fe489f592ac8e2159446c6bb1ab01855076c3b6c39ec"
}
},
{
"id": "combat.flying_projectile",
"title": "飞行物、目标、地形碰撞、命中和回收",
"subsystem": "combat",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonFlyModule.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonPlayerEventHandler.cpp",
"GameLib/ActorInstanceFly.cpp",
"GameLib/FlyingInstance.cpp",
"GameLib/FlyingData.cpp",
"GameLib/FlyTarget.cpp",
"GameLib/FlyingObjectManager.cpp",
"UserInterface/Packet.h"
],
"symbols": [
"CPythonPlayerEventHandler::OnSetFlyTarget",
"CPythonPlayerEventHandler::OnShoot",
"CPythonNetworkStream::RecvAddFlyTargetingPacket",
"CPythonNetworkStream::RecvFlyTargetingPacket",
"CPythonNetworkStream::RecvCreateFlyPacket",
"CPythonNetworkStream::SendShootPacket",
"CPythonNetworkStream::SendAddFlyTargetingPacket",
"CPythonNetworkStream::SendFlyTargetingPacket",
"CFlyingData::LoadScriptFile",
"CFlyingInstance::Create",
"CFlyingInstance::Update",
"CFlyingManager::CreateIndexedFly",
"CFlyTarget::NotifyTargetClear"
]
},
"implementation": {
"files": [
"project/fly_object.gd",
"project/world_shot.gd",
"project/fx/skill_fx.gd",
"project/net_play.gd",
"project/net_world.gd",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/entity_store.cpp",
"project/fly_test.gd",
"project/fly_reset_test.gd",
"project/test_exp_fly_parity.gd",
"project/fly_target_bounds_test.gd"
],
"symbols": [
"FlyObject.FlyData",
"FlyObject.FlyData.load_msf",
"FlyObject.FlyInstance.create",
"FlyObject.FlyInstance.update",
"FlyObject.FlyManager.spawn",
"FlyObject.FlyManager.clear_for_map_change",
"NetWorld._on_fly",
"NetWorld._indexed_fly_data",
"NetWorld._on_fly_targeting",
"NetPlay._emit_swing",
"NetPlay._send_fly_target",
"NetPlay.send_fly_targeting"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/combat.flying-projectile.md",
"tests": [
"project/fly_test.gd",
"project/fly_reset_test.gd",
"project/test_exp_fly_parity.gd",
"project/fly_target_bounds_test.gd",
"project/test_click_target_effect_parity.gd",
"project/combat_fx_test.gd",
"project/fly_data_msf_test.gd"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"动作事件 FLY 的 .msf 文件到 owner/skill/骨骼起点的完整创建链;索引飞行的 .msf 参数已接入 GC_CREATE_FLY",
"FIRE_CRACKER/AUTO_FIRE 的 CreateIndexedFly 分支、spreading/cone/roll/angular velocity 和 attach/tail 表现",
"hit-on-another-monster 动态 Actor 碰撞、穿透集合和另一目标回调",
"目标包围球中心、目标删除/VID 重用和不可见 shooter 的生命周期矩阵",
"GC_FLY_TARGETING/ADD/CREATE 的乱序、真实包序和 sequence 副作用",
"bombeffect/attach/tail/爆点资源与回收时序",
"真实服务端弓箭 FLY/SHOOT/CREATE_FLY 闭环"
],
"notes": "已完成飞行物数据、目标、弹道、背景碰撞、命中/穿透、网络目标追加和地图清理的逐分支静态审计;本轮按 PythonEffectModule/playersettingmodule 接入 .msf 解析、索引资源映射并修正 FLY 枚举,同时解除 FlyInstance handler 自捕获环,fly_reset_test 退出已无 ObjectDB 泄漏。动态 Actor 碰撞、动作事件 FLY 起点/owner/skill、test_exp_fly_parity 旧 fixture 清理和完整特效生命周期仍使合同保持 PARTIAL。",
"fingerprints": {
"reference": "78aa9c45552beea0f552c072bdf663049fb14d1e7ca8bd7c916bdfda7f6d8339",
"implementation": "c9ef1ee8219d239c2b1abe821e8fac5a4d562ba407cabfb4054135a8c768ab7c",
"tests": "b6106f8af47ea86f4c289e56455da78642f2ee35e074dbd073a640171e55aec8"
}
},
{
"id": "combat.affect_status",
"title": "受击、眩晕、击倒、状态旗标和状态图标",
"subsystem": "combat",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/AffectFlagContainer.cpp",
"UserInterface/InstanceBase.cpp",
"UserInterface/InstanceBaseEffect.cpp",
"UserInterface/InstanceBaseBattle.cpp",
"UserInterface/NetworkActorManager.cpp",
"UserInterface/PythonNetworkStreamPhaseGameActor.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/Packet.h",
"GameLib/ActorInstanceBattle.cpp"
],
"symbols": [
"CAffectFlagContainer::CopyData",
"CAffectFlagContainer::Set",
"CAffectFlagContainer::IsSet",
"CInstanceBase::SetAffectFlagContainer",
"CInstanceBase::__SetNormalAffectFlagContainer",
"CInstanceBase::__SetStoneSmokeFlagContainer",
"CInstanceBase::__ClearAffects",
"CInstanceBase::SetStateFlags",
"CInstanceBase::__CanProcessNetworkStatePacket",
"CPythonNetworkStream::RecvDamageInfoPacket",
"CPythonNetworkStream::RecvAffectAddPacket",
"CPythonNetworkStream::RecvAffectRemovePacket",
"CActorInstance::__HitGood",
"CActorInstance::__HitGreate",
"CActorInstance::__HitStone"
]
},
"implementation": {
"files": [
"project/combat_status_effect_system.gd",
"project/ui/hit_reaction.gd",
"project/ui/player_view.gd",
"project/ui/mob_view.gd",
"project/net_play.gd",
"project/net_world.gd",
"project/game_scene.gd",
"project/hud.gd",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/m2_client.cpp",
"project/ui/char_status_ui.gd"
],
"symbols": [
"EntityStore::mut_affect_flags",
"EntityStore::mut_stun",
"EntityStore::mut_affect_add",
"EntityStore::mut_affect_remove",
"NetWorld::_apply_field_updates",
"NetWorld::_on_damage",
"NetPlay::_can_process_network_state",
"NetPlay::_process_attack_success",
"HitReaction.good",
"HitReaction.greate",
"PlayerView.set_affect_flags",
"MobView.set_affect_flags"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/combat.affect-status.md",
"tests": [
"project/test_combat_status_effect_parity.gd",
"project/damage_effect_test.gd",
"project/combat_fx_test.gd",
"project/test_mob_combat_reaction_parity.gd",
"project/test_combat_death_pickup_entry_parity.gd",
"project/hit_view_test.gd",
"build/extension/net_entity_test",
"build/extension/net_classic_session_test"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"隔离 combat_status_effect_system 的本地权威结算,改为服务端 affect/damage 包驱动",
"补齐 affect bit 到附着特效、图标、骨骼、stone smoke 和建筑 no-op 的逐位资源矩阵",
"统一远端 stunned/knockdown/skill-cancel 的动作和网络状态门控",
"修复 hit_view_test 前向 damage 资源/动作锁时序失败",
"补齐重复、乱序、duration、死亡、换图、重连和 VID 重用清理测试"
],
"notes": "已完成 40250 AffectFlagContainer、Actor affect/受击动作、damage/affect 协议和当前 native/GDScript 链的逐分支审计;确认本地状态结算器的数据权威错误,且 hit_view_test 有 1 项失败。合同保持 PARTIAL,本轮仅登记审计结果,未修改实现。",
"fingerprints": {
"reference": "a17de5fbf004c1c6b799300ee026c44250c7be01839e79f4e2df927c65748301",
"implementation": "c0d2d09d88eddafb7e3586b6a5894e95d3d143182f486f7572c790b9c031bd4f",
"tests": "c8c6a05be637f15e1ca812a4a59cd5f3be900104ce325e729de3690beb594d3b"
}
},
{
"id": "skill.player_ui_cooldown",
"title": "技能栏、快捷键、等级、冷却和禁止条件",
"subsystem": "skill",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonSkill.cpp",
"UserInterface/PythonPlayerSkill.cpp",
"UserInterface/PythonPlayer.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/InstanceBase.cpp",
"UserInterface/Packet.h"
],
"symbols": [
"CPythonSkill::RegisterSkillTable",
"CPythonSkill::SSkillData::GetSkillCoolTime",
"CPythonSkill::SSkillData::GetTargetCount",
"CPythonSkill::SSkillData::IsCanUseSkill",
"CPythonPlayer::ClickSkillSlot",
"CPythonPlayer::__CanUseSkill",
"CPythonPlayer::__CheckSkillUsable",
"CPythonPlayer::__UseSkill",
"CPythonPlayer::__RunCoolTime",
"CPythonPlayer::SetSkillLevel_",
"CPythonPlayer::SetSkillCoolTime",
"CPythonPlayer::EndSkillCoolTime",
"CPythonNetworkStream::RecvSkillLevelNew",
"CPythonNetworkStream::RecvSkillCoolTimeEnd",
"CInstanceBase::CanUseSkill"
]
},
"implementation": {
"files": [
"project/player_skill.gd",
"project/ui/skill_table.gd",
"project/ui/skill_ui.gd",
"project/ui/quickbar.gd",
"project/net_play.gd",
"project/net_client_fake.gd",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/m2_client.cpp",
"extension/src/net/wire.h"
],
"symbols": [
"SkillTable.load_file",
"SkillTable.cooldown_of",
"SkillTable.can_level_up_skill",
"PlayerSkill.click_skill_slot",
"PlayerSkill.can_use_skill",
"PlayerSkill.check_skill_usable",
"PlayerSkill.use_skill",
"PlayerSkill.use_guild_skill",
"Quickbar.restore_from_server",
"Quickbar._activate_slot",
"Quickbar._activate_guild_skill",
"Quickbar._skill_cooldown",
"Quickbar._on_cd_end",
"NetPlay.skill_context",
"EntityStore::mut_skill_levels",
"EntityStore::mut_skill_group",
"EntityStore::mut_quickslot_set",
"EntityStore::mut_skill_cooldown_end"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/skill.player-ui-cooldown.md",
"tests": [
"project/player_skill_test.gd",
"project/skill_test.gd",
"project/netplay_test.gd",
"project/test_skill_inventory_parity.gd",
"project/test_taskbar_quickslot_parity.gd",
"project/test_status_skill_reset_parity.gd",
"project/mobile_gesture_test.gd",
"project/playable_combat_test.gd",
"project/guild_war_skill_test.gd",
"project/test_full_skill_flow.gd",
"build/extension/net_entity_test",
"build/extension/net_classic_session_test"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"CanUseSkill 五类动作门已补齐;仍需把真实 shape/图形动作字段和拒绝副作用接入端到端回归",
"POINT_CASTING_SPEED 已暴露并按 40250 __RunCoolTime 修正;仍需真实点变更/冷却回包时序 fixture",
"补齐 skill/group/quickslot/cooldown-end 乱序、重复、断线、重连和换图测试",
"建立全部技能 ID/grade/.msk 公式/动作/目标追加和服务端 damage 顺序快照"
],
"notes": "已完成 40250 技能数据、三层施放校验、CanUseSkill、casting-speed 冷却公式、快捷栏、普通/公会技能发送入口、skill/group/cooldown 协议和 FUNC_SKILL→CG_USE_SKILL 动作/意图包序的逐分支审计;CanUseSkill 五类动作门、POINT_CASTING_SPEED 桥接和公会技独立路径已实现,但真实拒绝/乱序/跨生命周期 fixture、完整动作资源包序和服务端 damage 顺序仍使合同保持 PARTIAL。",
"fingerprints": {
"reference": "41123a16a7c43e570f57854c4165bc04bb2dfb875efb0bf34d9f5e6afd08e383",
"implementation": "bad2f21e5c0ea4de37508d94ed1724931696d9409db5f487a708da0bdfbd3cc7",
"tests": "0746ef57d85bea05245ba44d178e24790d28623c40559520508db7acbd7970f0"
}
},
{
"id": "item.inventory_equipment_state",
"title": "背包、装备栏、物品状态、槽位和穿脱装备",
"subsystem": "item",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/GameType.h",
"UserInterface/PythonPlayer.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamPhaseGameItem.cpp",
"UserInterface/PythonItem.cpp",
"GameLib/ItemData.cpp",
"GameLib/ItemManager.cpp",
"UserInterface/Packet.h"
],
"symbols": [
"TItemPos::IsValidCell",
"TItemPos::IsEquipCell",
"CPythonPlayer::SetItemData",
"CPythonPlayer::SetItemCount",
"CPythonPlayer::SetItemMetinSocket",
"CPythonPlayer::SetItemAttribute",
"CPythonPlayer::IsEquipItemInSlot",
"CPythonNetworkStream::RecvItemSetPacket",
"CPythonNetworkStream::RecvItemSetPacket2",
"CPythonNetworkStream::RecvItemUpdatePacket",
"CPythonNetworkStream::__RefreshInventoryWindow",
"CPythonNetworkStream::SendItemUsePacket",
"CPythonNetworkStream::SendItemMovePacket",
"CPythonNetworkStream::SendItemDropPacket",
"CPythonNetworkStream::SendItemUseToItemPacket",
"CItemManager::GetItemDataPointer",
"CItemData::IsEquipment"
]
},
"implementation": {
"files": [
"project/ui/inventory_ui.gd",
"project/ui/item_list.gd",
"project/ui/equip_model.gd",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_stream.cpp",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/m2_client.cpp",
"extension/src/net/m2_client.h",
"extension/src/net/net_bounds.h",
"extension/src/net/item_action_rules.h",
"extension/tests/net_bounds_test.cpp",
"extension/tests/net_entity_test.cpp",
"extension/tests/net_classic_session_test.cpp",
"project/inventory_ui_test.gd",
"project/equip_model_test.gd",
"project/game_scene.gd",
"project/item_tooltip_test.gd",
"project/view_equipment_ui_test.gd",
"project/item_action_sound_parity_test.gd"
],
"symbols": [
"EntityStore::mut_item_set",
"EntityStore::mut_item_del",
"EntityStore::mut_item_update",
"EntityStore::mut_slot",
"ClassicParser::on_gc",
"ClassicSession::send_item_use",
"ClassicSession::send_item_move",
"ClassicSession::send_item_drop",
"ClassicSession::send_item_use_to_item",
"M2Client::move_item",
"M2Client::use_item",
"M2Client::drop_item",
"M2Client::drop_item_count",
"M2Client::use_item_to_item",
"M2Client::item_action_allowed",
"M2Client::is_equip_item_in_slot",
"M2Client::set_item_proto",
"M2Client::item_action_sound_for_item",
"M2Client::emit_item_action_sound",
"mtnet::item_action::use_sound_type",
"mtnet::item_action::drop_sound_type",
"InventoryUI::_to_wire",
"InventoryUI::refresh",
"InventoryUI::_on_item_highlighted",
"InventoryUI::use",
"EquipModel::_refresh"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "MAPPED",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/item.inventory-equipment-state.md",
"tests": [
"extension/tests/net_bounds_test.cpp",
"extension/tests/net_entity_test.cpp",
"extension/tests/net_classic_session_test.cpp",
"project/inventory_ui_test.gd",
"project/equip_model_test.gd",
"project/item_tooltip_test.gd",
"project/view_equipment_ui_test.gd",
"project/item_action_sound_parity_test.gd"
],
"last_test_result": "PASS"
},
"remaining": [
"M2Client native use/move 音效已按 item_proto 分类并接入 Audio;完整 m2dev/经典 SendSequence 包序和服务器拒绝副作用仍需证明;主角行动门与 dedicated/global equipment cell 的交易/商店/攻击中门及 ItemManager 装备分类已补齐",
"RecvItemUpdatePacket 的双重刷新已通过 InvChange 快照和 inventory_state_changed 适配;仍未证明所有旧 inventory_changed 监听者都消费了中间状态;Set2 highlight 已通过 item_highlighted 队列和 InventoryUI 消费路径接通",
"空槽 Update 虽在 EntityStore 保留,但 get_inventory 只暴露非零 vnum,未覆盖参考端对空槽字段的读取语义",
"新装备、龙魂和腰带的经典/m2dev 双协议乱序、拒绝、换图重建测试",
"服务器拒绝或拖拽中断后的 UI 回滚与窗口刷新顺序"
],
"notes": "本轮回归确认非法 cell/空槽 Update/native 行动门/装备分类/Set2 highlight/未知 vnum 校验和 item_proto 音效分类未回归;完整 sequence、旧监听者中间快照、空槽字段 API、扩展槽位乱序、服务器拒绝回滚和断线回滚仍保持 PARTIAL。view_equipment_ui_test 与 netbridge_test 通过但各有 5 个 ObjectDB 泄漏。",
"fingerprints": {
"reference": "1278f3e04f31a69efd71c1d5542e3d697e279575675031bdf96d30aaa0d1ff75",
"implementation": "b8bc1a86bd1781601e4faa66724fe3674bb5b6658d82f6e8c8b57bbae83b261e",
"tests": "b486458e209c415e1373ae96e83a6be7c558aca8afb99822baba350c89fc24be"
}
},
{
"id": "item.consumable_action",
"title": "使用、堆叠、拆分、快捷使用和消耗品反馈",
"subsystem": "item",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/GameType.h",
"UserInterface/PythonNetworkStreamPhaseGameItem.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonPlayer.cpp",
"UserInterface/PythonPlayerModule.cpp",
"UserInterface/PythonItem.cpp",
"UserInterface/Packet.h"
],
"symbols": [
"CPythonPlayer::SetQuickPage",
"CPythonPlayer::RequestUseLocalQuickSlot",
"CPythonPlayer::RequestAddLocalQuickSlot",
"CPythonPlayer::RequestAddToEmptyLocalQuickSlot",
"CPythonPlayer::RequestDeleteGlobalQuickSlot",
"CPythonPlayer::RequestMoveGlobalQuickSlotToLocalQuickSlot",
"CPythonPlayer::MoveItemData",
"CPythonNetworkStream::SendItemUsePacket",
"CPythonNetworkStream::SendItemUseToItemPacket",
"CPythonNetworkStream::SendItemMovePacket",
"CPythonNetworkStream::SendShopSellPacket",
"CPythonNetworkStream::SendShopSellPacketNew",
"CPythonNetworkStream::RecvItemUsePacket",
"CPythonNetworkStream::RecvItemUpdatePacket",
"CPythonNetworkStream::RecvQuickSlotAddPacket",
"CPythonNetworkStream::RecvQuickSlotDelPacket",
"CPythonNetworkStream::RecvQuickSlotMovePacket"
]
},
"implementation": {
"files": [
"project/consumable_system.gd",
"project/item_split_system.gd",
"project/ui/quickbar.gd",
"project/ui/inventory_ui.gd",
"project/ui/shop_ui.gd",
"project/net_play.gd",
"extension/src/net/m2_client.cpp",
"extension/src/net/m2_client.h",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/entity_store.cpp",
"extension/tests/net_classic_session_test.cpp",
"project/test_consumables_parity.gd",
"project/test_item_split_parity.gd",
"project/test_taskbar_quickslot_parity.gd",
"project/test_skill_inventory_parity.gd",
"project/skill_test.gd",
"project/mobile_gesture_test.gd",
"project/complex_item_drop_test.gd",
"project/netplay_test.gd"
],
"symbols": [
"ConsumableSystem::toggle_elixir",
"ConsumableSystem::process_elixir_recovery",
"ConsumableSystem::use_dragon_god_potion",
"ConsumableSystem::use_warp_scroll",
"ItemSplitSystem::split_item",
"ItemSplitSystem::quick_sell_item",
"Quickbar::assign",
"Quickbar::_activate_slot",
"Quickbar::_clear",
"Quickbar::_swap",
"InventoryUI::move_to",
"InventoryUI::use",
"ShopUI::_send_sell",
"M2Client::quickslot_add",
"M2Client::quickslot_del",
"M2Client::quickslot_swap",
"M2Client::use_item",
"M2Client::use_item_to_item",
"M2Client::move_item",
"M2Client::shop_sell",
"ClassicSession::send_item_use",
"ClassicSession::send_item_use_to_item",
"ClassicSession::send_item_move",
"ClassicSession::send_shop_sell",
"EntityStore::mut_quickslot_set",
"EntityStore::mut_quickslot_del",
"EntityStore::mut_quickslot_swap"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/item.consumable-action.md",
"tests": [
"project/test_consumables_parity.gd",
"project/test_item_split_parity.gd",
"project/test_taskbar_quickslot_parity.gd",
"project/complex_item_drop_test.gd",
"project/netplay_test.gd"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"快捷栏请求现在等待 GC_QUICKSLOT_* 后重建本地状态;仍需真实拒绝、重复、乱序和断线/换图回包 fixture",
"消耗品 ITEM_USE 后 ITEM_UPDATE/AFFECT/POINT/SPECIAL_EFFECT 的真实包序和 UI 反馈",
"堆叠拆分已统一为 CG_ITEM_MOVE 请求并等待服务器确认;快捷出售已统一为 CG_SHOP SELL/SELL2,价格、金币和物品变化仍待真实回包 fixture",
"消耗品 native action gate 与装备类型门已补齐;物品音效和 SendSequence 的资源/包序证据仍缺",
"断线、换图、重复回包、请求失败和延迟期间的状态清理/回滚",
"快捷栏乐观更新和页面取模差异已修复;quickbar 资源泄漏、完整生命周期清理和真实服务器确认 fixture 仍待补齐;complex_item_drop_test 的购买模式卖出目标判定已修复"
],
"notes": "已将拆分、快捷出售和快捷栏增删交换统一到 request→server/GC 确认路径,并修复购买模式误发出售请求;快捷栏使用、消耗品真实反馈包序、音效/sequence 和断线清理仍使合同保持 PARTIAL。",
"fingerprints": {
"reference": "46e51b7a21012ae531c21f8a752fde5592659081feec6d67edfa86c19f7c2082",
"implementation": "0139c7d3d92f8d812eaef51d3d8dfb70f0143f0b4660fa21cbca4c2f16284bfd",
"tests": "bc8250918b7945d30b8174a2206ad3f95f9f9fe6523217e312e9090317a03f0c"
}
},
{
"id": "item.drop_pickup",
"title": "地面掉落、拾取、光标、归属和掉落名条",
"subsystem": "item",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonItem.cpp",
"UserInterface/PythonItem.h",
"UserInterface/PythonNetworkStreamPhaseGameItem.cpp",
"UserInterface/PythonPlayer.cpp",
"UserInterface/PythonPlayerInput.cpp",
"UserInterface/PythonPlayerInputMouse.cpp",
"UserInterface/PythonTextTail.cpp",
"GameLib/ItemData.cpp",
"GameLib/ItemManager.cpp",
"UserInterface/Packet.h"
],
"symbols": [
"CPythonNetworkStream::RecvItemGroundAddPacket",
"CPythonNetworkStream::RecvItemGroundDelPacket",
"CPythonNetworkStream::RecvItemOwnership",
"CPythonItem::CreateItem",
"CPythonItem::SetOwnership",
"CPythonItem::DeleteItem",
"CPythonItem::__Pick",
"CPythonItem::GetGroundItemPosition",
"CPythonPlayer::SendClickItemPacket",
"CPythonPlayer::__OnPressItem",
"CPythonPlayerInput::OnMouseButton",
"CPythonTextTail::RegisterItemTextTail",
"CPythonTextTail::SetItemTextTailOwner"
]
},
"implementation": {
"files": [
"project/mob_drop_scatter.gd",
"project/ui/ground_items.gd",
"project/ui/mouse_controller.gd",
"project/player_controller.gd",
"project/net_play.gd",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/m2_client.cpp",
"extension/tests/net_entity_test.cpp",
"extension/tests/net_classic_session_test.cpp",
"project/test_ground_pickup_parity.gd",
"project/mob_death_drop_test.gd",
"project/p2b_test.gd",
"project/text_tail_arrange_test.gd"
],
"symbols": [
"GroundItems::_on_added",
"GroundItems::_on_removed",
"GroundItems::try_pickup",
"GroundItems::try_pickup_vid",
"GroundItems::pick_at",
"GroundItems::clear_for_map_change",
"NetPlay::pick_ground_item",
"NetPlay::reserve_click_item",
"PlayerController::_update_hover_cursor",
"EntityStore::mut_ground_add",
"EntityStore::mut_ground_del",
"EntityStore::mut_ground_owner",
"ClassicParser::on_gc",
"ClassicSession::send_item_pickup",
"M2Client::pickup_item"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "GAP",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/item.drop-pickup.md",
"tests": [
"project/test_ground_pickup_parity.gd",
"project/mob_death_drop_test.gd",
"project/p2b_test.gd",
"project/text_tail_arrange_test.gd",
"extension/tests/net_entity_test.cpp",
"extension/tests/net_classic_session_test.cpp"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"使用 CItemData::GetDropModelThing、drop effect、drop sound、法线和掉落动画,而不是通用占位模型",
"未知 vnum 拒绝、ItemManager anti-flag、party member 所有权和 observer/native action gate",
"模型实际相交 + TextTail Pick 与当前 64px 投影点选的边界测试",
"统一 20 像素/3m/5m、server cm 与 Godot m 的距离链,避免双层拾取阈值不一致",
"CG_ITEM_PICKUP→GC_ITEM_GROUND_DEL→GC_ITEM_GET/ITEM_SET/UPDATE 的真实包序",
"拾取失败、重复请求、ownership 乱序、换图/断线旧 VID 和 queue_free 延迟清理",
"修复 p2b_test 退出时的 2 个 ObjectDB 泄漏,并覆盖掉落名条颜色不被近距离刷新覆盖"
],
"notes": "本轮完成掉落/拾取/所有权/名条/协议逐分支审计;基础回归通过但资源、权限和反馈链仍不等价,合同保持 PARTIAL。",
"fingerprints": {
"reference": "18e94abecd2cbd63407ec94fe4f2b692505b0ea156e3149fcfa426920a10856b",
"implementation": "664112e5403d65b499075c104656efec9e2920da2f5dca0edec66339e9005b66",
"tests": "0713f0aeef0e53fdf447537c2a8e785559269b45ca63825c4b507382fbc95019"
}
},
{
"id": "item.refine_socket_costume",
"title": "强化、镶嵌、属性、时装和外观换装",
"subsystem": "item",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamModule.cpp",
"UserInterface/PythonNetworkStreamPhaseGameItem.cpp",
"UserInterface/PythonPlayer.cpp",
"UserInterface/PythonPlayerModule.cpp",
"UserInterface/PythonItem.cpp",
"UserInterface/PythonItemModule.cpp",
"UserInterface/PythonNetworkStreamPhaseGameActor.cpp",
"UserInterface/InstanceBase.cpp",
"UserInterface/Packet.h",
"GameLib/ItemData.cpp",
"GameLib/ItemData.h",
"../../Client/Eternexus/root/uirefine.py",
"../../Client/Eternexus/root/uiinventory.py",
"../../Server/metin2/src/server/game/src/char_item.cpp",
"../../Server/metin2/src/server/game/src/item.cpp",
"../../Server/metin2/src/server/game/src/item_attribute.cpp",
"../../Server/metin2/server/share/conf/item_proto.txt"
],
"symbols": [
"CPythonNetworkStream::SendRefinePacket",
"CPythonNetworkStream::RecvRefineInformationPacket",
"CPythonNetworkStream::RecvRefineInformationPacketNew",
"CPythonPlayer::SetItemData",
"CPythonPlayer::SetItemCount",
"CPythonPlayer::SetItemMetinSocket",
"CPythonPlayer::SetItemAttribute",
"playerCanRefine",
"playerCanAttachMetin",
"CItemData::GetSocket",
"CItemData::GetSocketCount",
"CItemData::GetRefine",
"CItemData::HasNextGrade",
"CInstanceBase::SetArmor",
"CInstanceBase::SetHair",
"CInstanceBase::__GetRefinedEffect"
]
},
"implementation": {
"files": [
"project/ui/refine_ui.gd",
"project/refine_effect_system.gd",
"project/item_attr_system.gd",
"project/item_enchant_system.gd",
"project/jewelry_socket_system.gd",
"project/costume_system.gd",
"project/costume_attr_transfer_system.gd",
"project/wardrobe_transmog_system.gd",
"project/ui/equip_model.gd",
"extension/src/net/m2_client.cpp",
"extension/src/net/m2_client.h",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_session.h",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/wire_classic.h",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/net_bounds.h"
],
"symbols": [
"RefineUI::_on_ask",
"RefineUI::_accept",
"RefineUI::cancel_refine",
"M2Client::refine",
"ClassicSession::send_refine",
"EntityStore::mut_refine",
"ItemAttrSystem::apply_add_attribute",
"ItemAttrSystem::apply_change_attribute",
"ItemAttrSystem::apply_blessing_marble",
"ItemEnchantSystem::apply_scroll",
"JewelrySocketSystem::drill_socket",
"JewelrySocketSystem::insert_gem",
"CostumeSystem::equip_costume",
"CostumeSystem::unequip_costume",
"WardrobeTransmogSystem::apply_transmutation"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "GAP",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/item.refine-socket-costume.md",
"tests": [
"project/test_refine_parity.gd",
"project/test_refine_effect_parity.gd",
"project/test_item_attr_parity.gd",
"project/test_item_enchant_manager_parity.gd",
"project/test_jewelry_socket_parity.gd",
"project/test_costume_system_parity.gd",
"project/test_costume_attr_transfer_parity.gd",
"project/test_wardrobe_transmog_parity.gd",
"extension/tests/net_classic_session_test.cpp",
"extension/tests/net_entity_test.cpp",
"extension/tests/net_bounds_test.cpp"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"覆盖 M2Client::refine(255,255) 通过后的真实取消包和服务器回包",
"按 GC_REFINE_INFORMATION(95) 与 GC_REFINE_INFORMATION_NEW(119) 分开解析并覆盖两种包",
"将属性/镶嵌操作改为 ItemManager/use subtype + CG_ITEM_USE/ITEM_USE_TO_ITEM 请求和 GC_ITEM_UPDATE 权威回写",
"把时装穿戴、属性、过期和 bonus 接入服务端 item/affect/point 与 actor armor/weapon/hair 更新链",
"让强化等级/socket/type/subtype 驱动实际装备模型、强化特效 attach/clear,并覆盖资源缺失清理",
"补充拒绝、断线、重复/乱序回包、换图、过期和服务端回滚测试",
"将 wardrobe/transmog 自定义扩展与 40250 parity 分开标注,除非补齐对应服务端协议和角色部件同步"
],
"notes": "本轮已修复 M2Client::refine 对 40250 取消 sentinel(255,255) 的错误拦截,并补充边界/EntityStore 回归;属性、socket、时装和幻化仍绕过服务器权威回包,合同保持 PARTIAL。",
"fingerprints": {
"reference": "e76ca9bb01f7b7838943820f3fe5633f68a32364c3f3ece48cbc66bf74a6a247",
"implementation": "176ad923087d84b8f11dee97d2bd198c495d91014d8b720d3bf58acd0bd26fb8",
"tests": "6bb13ce9e81271dfa30eaaab03c5cf5e801ef22965c32d4b4d38af95f6874bfb"
}
},
{
"id": "npc.dialog_shop",
"title": "NPC 交互、普通商店、个人商店和购买流程",
"subsystem": "npc",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonNonPlayer.cpp",
"UserInterface/PythonShop.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamPhaseGameItem.cpp",
"UserInterface/PythonNetworkStreamModule.cpp",
"UserInterface/PythonPlayerInput.cpp",
"UserInterface/PythonPlayerInputMouse.cpp",
"UserInterface/Packet.h",
"../../Client/Eternexus/root/uishop.py",
"../../Client/Eternexus/root/uiprivateshopbuilder.py",
"../../Client/Eternexus/root/uiinventory.py",
"../../Client/Eternexus/root/playersettingmodule.py"
],
"symbols": [
"CPythonNetworkStream::RecvShopPacket",
"CPythonNetworkStream::SendShopEndPacket",
"CPythonNetworkStream::SendShopBuyPacket",
"CPythonNetworkStream::SendShopSellPacket",
"CPythonNetworkStream::SendShopSellPacketNew",
"CPythonNetworkStream::SendOnClickPacket",
"CPythonNetworkStream::RecvShopSignPacket",
"CPythonShop::SetItemData",
"CPythonShop::BuildPrivateShop",
"PrivateShopBuilder::OnSelectEmptySlot",
"ShopDialog::Open",
"ShopDialog::SelectItemSlot",
"ShopDialog::UnselectItemSlot"
]
},
"implementation": {
"files": [
"project/npc_shop_system.gd",
"project/private_shop_system.gd",
"project/ui/shop_ui.gd",
"project/ui/private_shop_ui.gd",
"project/net_play.gd",
"project/net_world.gd",
"project/ui/quest_dialog.gd",
"extension/src/net/m2_client.cpp",
"extension/src/net/m2_client.h",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_session.h",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/wire_classic.h",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h"
],
"symbols": [
"NpcShopSystem::buy_item",
"NpcShopSystem::sell_item",
"ShopUI::_drop_to_sell",
"ShopUI::_buy_slot",
"ShopUI::_request_sell",
"PrivateShopUI::_ok",
"NetPlay::_on_pick",
"NetWorld::_refresh_shop_sign",
"M2Client::click_npc",
"M2Client::shop_buy",
"M2Client::shop_sell",
"M2Client::open_private_shop",
"EntityStore::mut_shop_update_item",
"EntityStore::mut_shop_sign"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "GAP",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "GAP",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/npc.dialog-shop.md",
"tests": [
"project/test_npc_shop_parity.gd",
"project/test_npc_shop_flow.gd",
"project/test_npc_dialog_shop_ui_parity.gd",
"project/private_shop_ui_test.gd",
"project/test_private_shop_bundle_parity.gd",
"project/shop_rules_test.gd",
"project/p8_test.gd",
"project/complex_item_drop_test.gd",
"extension/tests/net_classic_session_test.cpp",
"extension/tests/net_entity_test.cpp"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"修复 EntityStore::mut_shop_update_item 对 40..119 全局位置的 tab 映射,并补充多货架增量更新测试",
"禁止 ShopUI 在购买模式接收拖放出售请求,并补充输入路径回归",
"按 shop VID 区分 NPC、他人个人商店和自己的个人商店,复现 40250 的按钮/关闭分支",
"把带 shop_sign 的 PC 点击路由到 SendOnClickPacket 并覆盖招牌创建、点击和 GC_END 清理",
"为买卖请求补齐动作门控、实际库存/anti flag/商店状态校验和服务器结果回写",
"移除 live 路径对硬编码 NPC 库存与本地金币/背包 helper 的权威依赖",
"补充 START/START_EX/UPDATE/ERROR/END、拒绝、售罄、断线、重连和乱序包的端到端测试",
"补充个人商店死亡/骑乘/战斗/bundle/重复源格校验和服务端拒绝清理测试"
],
"notes": "已完成 40250 与当前端的 NPC 点击、脚本对话、普通/多货架商店、买卖、个人商店招牌和摆摊提交逐分支/协议/状态审计。协议骨架和本地 UI 测试存在,但多货架更新、个人商店点击/状态、动作门控及本地直改旁路使合同保持 PARTIAL。",
"fingerprints": {
"reference": "75f3901cc3c0f8cdb60bb5d5c5c04e695776ce365d9988ce83e4ea64f24e4dee",
"implementation": "3396b617791fddb530e44180457248d2222fd3e971a1bd2b08503486de73213d",
"tests": "3829ff07408c019b3a4a65fa8d4f283fe723342bcc2d1ed51ec4993d7f9c1874"
}
},
{
"id": "social.chat_text_tail",
"title": "公聊、私聊、聊天气泡、名字尾标和文本布局",
"subsystem": "social",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonChat.cpp",
"UserInterface/PythonChat.h",
"UserInterface/PythonTextTail.cpp",
"UserInterface/PythonTextTail.h",
"UserInterface/InstanceBaseEffect.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.h",
"UserInterface/Packet.h",
"../../Client/Eternexus/root/game.py",
"../../Client/Eternexus/root/uiwhisper.py",
"../../Client/Eternexus/root/localeInfo.py",
"../../Client/Eternexus/root/colorInfo.py"
],
"symbols": [
"CPythonNetworkStream::RecvChatPacket",
"CPythonNetworkStream::RecvWhisperPacket",
"CPythonChat::AppendChat",
"CPythonChat::AppendChatWithDelay",
"CPythonChat::AppendWhisper",
"CPythonChat::IgnoreCharacter",
"CPythonChat::IsIgnoreCharacter",
"CPythonTextTail::RegisterChatTail",
"CPythonTextTail::RegisterInfoTail",
"CPythonTextTail::UpdateAllTextTail",
"CInstanceBase::RefreshTextTail",
"CInstanceBase::AttachTextTail",
"GameWindow::OnRecvWhisper"
]
},
"implementation": {
"files": [
"project/chat_tail.gd",
"project/text_tail.gd",
"project/text_tail_arrange.gd",
"project/name_tail_layout.gd",
"project/ui/chat_ui.gd",
"project/whisper_chat_system.gd",
"project/net_world.gd",
"project/game_scene.gd",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/m2_client.cpp",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_session.h",
"extension/src/net/classic/wire_classic.h"
],
"symbols": [
"EntityStore::mut_chat",
"EntityStore::mut_whisper",
"EntityStore::drain_chat",
"M2Client::say",
"M2Client::whisper",
"ChatUI::_on_submit",
"ChatUI::_on_chat",
"ChatUI::_on_whisper",
"WhisperChatSystem::receive_whisper",
"NetWorld::_bubble",
"NetWorld::register_info_tail",
"NetWorld::_expire_chat_tails",
"NetWorld::_reposition_name_tag",
"ChatTail::register_chat",
"ChatTail::register_info",
"TextTail::refresh_title",
"TextTail::refresh_level"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "GAP",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "GAP",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "GAP",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/social.chat-text-tail.md",
"tests": [
"project/chat_test.gd",
"project/chat_tail_test.gd",
"project/text_tail_test.gd",
"project/text_tail_arrange_test.gd",
"project/name_tail_layout_test.gd",
"project/guild_mark_tail_test.gd",
"project/test_whisper_chat_parity.gd",
"project/chat_mobile_ui_test.gd",
"project/test_chat_hyperlink_dice_parity.gd",
"build/extension/net_entity_test",
"build/extension/net_classic_session_test"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"补齐 whisper mode 0..5/0xFF 的独立会话、系统/GM/错误颜色、通知和服务端正文语义",
"补齐 live WhisperChatSystem 的窗口历史、任务栏按钮、未读闪烁、ignore 命令和发送结果状态",
"补齐无 VID INFO/NOTICE/BIG_NOTICE 专用提示、COMMAND 副作用和未知 VID 忽略分支",
"复现入站表情解析、跨帝国文字转换、辱骂过滤和 40250 延迟聊天队列",
"统一公共聊天颜色和文本前缀处理,发送失败不能生成成功回显",
"让聊天/信息尾标使用同一权威实体生命周期,补齐真实字体度量、屏幕投影和死亡/换图清理",
"补充重复/乱序/断线/重连、未知类型、超长文本和 whisper 错误端到端测试"
],
"notes": "已完成 40250 与当前端的公共聊天、私聊、忽略、聊天气泡、名字/称号/等级/公会名尾标及文本布局逐分支/协议/状态审计。已修正 whisper mode 映射、截断 GC_WHISPER 拒绝逻辑,并将 live ChatUI 的普通/GM whisper 接入独立会话和 ignore 判断;基础网络与静态尾标测试存在,但窗口通知、公共提示/过滤/表情/颜色和 Godot 字体布局差异使合同保持 PARTIAL。",
"fingerprints": {
"reference": "82802154cb44736cfa53a3dc2de5879c1fb9ccb4f47037b70827a2ba8262c9f8",
"implementation": "cd02d44596b377534a4200e0ef7002b3c6de8e7c328fbdd96afc7e9ed8a6cb8f",
"tests": "c86122ea9b1448a74f4af2a90bc25382ae7b477d6a2cc96b27c5ac3b48d97e39"
}
},
{
"id": "social.party_guild_messenger",
"title": "组队、公会、好友、Messenger 和徽章",
"subsystem": "social",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamPhaseGameActor.cpp",
"UserInterface/PythonGuild.cpp",
"UserInterface/PythonMessenger.cpp",
"UserInterface/PythonCharacterManager.cpp",
"UserInterface/GuildMarkDownloader.cpp",
"UserInterface/GuildMarkUploader.cpp",
"UserInterface/MarkManager.cpp",
"UserInterface/Packet.h",
"../../Client/Eternexus/root/uiparty.py",
"../../Client/Eternexus/root/uiguild.py",
"../../Client/Eternexus/root/uimessenger.py",
"../../Client/Eternexus/root/uiuploadmark.py",
"../../Client/Eternexus/root/game.py",
"../../Client/Eternexus/root/interfacemodule.py",
"../../Client/Eternexus/root/uitaskbar.py"
],
"symbols": [
"CPythonNetworkStream::RecvPartyPacket",
"CPythonNetworkStream::RecvGuildPacket",
"CPythonNetworkStream::RecvMessengerPacket",
"CPythonNetworkStream::SendPartyInvitePacket",
"CPythonNetworkStream::SendPartyParameterPacket",
"CPythonNetworkStream::SendGuildPacket",
"CPythonMessenger::OnFriendLogin",
"CPythonMessenger::OnFriendLogout",
"CPythonMessenger::SetMobile",
"CPythonCharacterManager::RefreshAllGuildMark",
"CGuildMarkDownloader::__LoginState_RecvMarkIndex",
"CGuildMarkDownloader::__LoginState_RecvMarkBlock",
"CGuildMarkDownloader::__SendMarkCRCList",
"CPythonGuild::UploadMark"
]
},
"implementation": {
"files": [
"project/party_management_system.gd",
"project/guild_skill_system.gd",
"project/guild_war_system.gd",
"project/guild_war_observer_arena_system.gd",
"project/messenger_system.gd",
"project/guild_mark_uploader_system.gd",
"project/ui/party_ui.gd",
"project/ui/guild_ui.gd",
"project/ui/friend_ui.gd",
"project/game_scene.gd",
"project/net_world.gd",
"project/app_flow.gd",
"extension/src/net/m2_client.cpp",
"extension/src/net/m2_client.h",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_session.h",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/classic_mark_client.cpp",
"extension/src/net/classic/classic_mark_client.h",
"extension/src/net/classic/wire_classic.h"
],
"symbols": [
"PartyManagementSystem::distribute_exp",
"PartyManagementSystem::set_member_role",
"GuildSkillSystem::donate_exp",
"GuildSkillSystem::use_skill",
"GuildWarSystem::record_score",
"GuildWarSystem::end_war",
"MessengerSystem::add_friend",
"MessengerSystem::set_friend_state",
"GuildMarkUploaderSystem::upload_guild_mark",
"PartyUI::_on_invite",
"GuildUI::_fill_members",
"GuildUI::_fill_war",
"FriendUI::_on_add",
"M2Client::party_invite",
"M2Client::party_answer",
"M2Client::party_set_state",
"M2Client::add_friend",
"M2Client::friend_answer",
"M2Client::guild_post_comment",
"M2Client::use_guild_skill",
"M2Client::download_guild_marks",
"M2Client::upload_guild_mark",
"EntityStore::mut_party_update",
"EntityStore::mut_friend_set",
"EntityStore::mut_guild_member",
"EntityStore::mut_guild_war"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "GAP",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/social.party-guild-messenger.md",
"tests": [
"project/test_party_management_parity.gd",
"project/test_messenger_parity.gd",
"project/test_guild_mark_uploader_parity.gd",
"project/guild_mark_test.gd",
"project/guild_mark_tail_test.gd",
"project/test_guild_war_parity.gd",
"project/test_guild_war_observer_arena_parity.gd",
"project/test_guild_skill_parity.gd",
"project/guild_creation_ui_test.gd",
"project/test_privilege_empire_guild_parity.gd",
"project/p8_test.gd",
"extension/tests/net_entity_test.cpp",
"extension/tests/net_classic_session_test.cpp"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"为 GC_PARTY/GC_MESSENGER/GC_GUILD 全分支补充真实回包、拒绝、重复、乱序和重连测试",
"禁止本地 party/guild skill/war helper 作为 live 权威,改为请求后等待服务端 state/affect/point 回写",
"统一 live FriendUI 与 MessengerSystem 的会话/忽略/在线/移动状态来源",
"补充公会权限拒绝、离会/踢人、公告/技能/金钱回包和邀请清理",
"补充公会战声明/开始/结束/list/point 的连续事件与服务端奖励验证",
"补充独立 mark client 的握手、index、CRC、压缩 block、损坏、断线、重试和角色刷新测试",
"上传会徽/战旗必须验证真实用户资源、权限和服务端完成回包,避免默认占位图成为成功状态"
],
"notes": "已完成 40250 与当前端的组队、公会、好友/Messenger、会徽/战旗及相关 UI/native 回包链审计。live native 主路径较完整,但本地队伍/技能/公会战/Messenger/会徽 helper 仍存在直接状态旁路,真实权限拒绝、mark 独立连接和重连清理证据不足,合同保持 PARTIAL。",
"fingerprints": {
"reference": "6223e48f6be2d1471133c76658c264f2eaf5fcef7e2870ee21f538ff81e0d0ec",
"implementation": "df7b3599aa8255d8134668158fda22e2914854e30b763adc1deceb9c345a6873",
"tests": "89f4d155ee7d3b4f3457ef2114aed642ef6030a7a7c226d5731fcc20cd2ba2dd"
}
},
{
"id": "social.exchange_safebox",
"title": "玩家交易、仓库、安全箱和金币确认",
"subsystem": "social",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonExchange.cpp",
"UserInterface/PythonSafeBox.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamPhaseGameItem.cpp",
"Client/Eternexus/root/uiexchange.py",
"Client/Eternexus/root/uisafebox.py",
"Client/Eternexus/root/game.py"
],
"symbols": [
"CPythonNetworkStream::RecvExchangePacket",
"CPythonNetworkStream::SendExchangeStartPacket",
"CPythonNetworkStream::SendExchangeElkAddPacket",
"CPythonNetworkStream::SendExchangeItemAddPacket",
"CPythonNetworkStream::SendExchangeAcceptPacket",
"CPythonNetworkStream::SendExchangeExitPacket",
"CPythonNetworkStream::SendSafeBoxCheckinPacket",
"CPythonNetworkStream::SendSafeBoxCheckoutPacket",
"CPythonNetworkStream::SendSafeBoxItemMovePacket",
"CPythonNetworkStream::RecvSafeBoxSetPacket",
"CPythonNetworkStream::RecvSafeBoxDelPacket",
"CPythonNetworkStream::RecvSafeBoxSizePacket",
"CPythonNetworkStream::RecvMallOpenPacket",
"CPythonNetworkStream::RecvMallItemSetPacket",
"CPythonNetworkStream::RecvMallItemDelPacket",
"CPythonExchange",
"CPythonSafeBox"
]
},
"implementation": {
"files": [
"project/player_trade_exchange_system.gd",
"project/safebox_storage_keeper_system.gd",
"project/ui/exchange_ui.gd",
"project/ui/safebox_ui.gd",
"project/ui/mall_ui.gd",
"extension/src/net/m2_client.cpp",
"extension/src/net/m2_client.h",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_session.h"
],
"symbols": [
"M2Client::exchange_start",
"M2Client::exchange_add_item",
"M2Client::exchange_add_gold",
"M2Client::exchange_accept",
"M2Client::exchange_cancel",
"M2Client::safebox_checkin",
"M2Client::safebox_checkout",
"M2Client::safebox_move",
"M2Client::mall_checkout",
"EntityStore::apply",
"ExchangeUI",
"SafeboxUI",
"MallUI",
"PlayerTradeExchangeSystem",
"SafeboxStorageKeeperSystem"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "GAP",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/social.exchange-safebox.md",
"tests": [],
"last_test_result": "PARTIAL"
},
"remaining": [
"补齐 GC_EXCHANGE/GC_SAFEBOX/GC_MALL 的拒绝、乱序、断线和回包回滚测试",
"将交易和仓库 UI 的 optimistic 状态改为由 native 回包重建并验证源位置",
"区分并隔离本地 helper,避免将交易结算、密码、库存和金条规则当作客户端 authority",
"补齐 exchange 动作门控、真实服务端物品/金币校验和商城 checkout 失败清理"
],
"notes": "已完成交易、仓库、安全箱、商城和金币确认的逐分支/协议/状态审计;native 主链存在但与 40250 仍是 PARTIAL,本轮未修改实现。",
"fingerprints": {
"reference": "32d03a88d33723e087656bdfcbe806507b2e88e5ef20e36211d9f2cbab26a745",
"implementation": "d2cad2b8bdf7fc421478157ea0085bef07a4979d3d3799ff81823e1dd16216ab",
"tests": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
},
{
"id": "quest.quest_event_navigation",
"title": "任务状态、事件、对话、HUD 和导航提示",
"subsystem": "quest",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonQuest.cpp",
"UserInterface/PythonEventManager.cpp",
"UserInterface/PythonEventManager.h",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonNetworkStreamEvent.cpp",
"UserInterface/PythonMiniMap.cpp",
"Client/Eternexus/root/uiquest.py",
"Client/Eternexus/root/interfacemodule.py",
"Client/Eternexus/root/uicharacter.py",
"Client/Eternexus/root/uiminimap.py",
"Client/Eternexus/root/atlasinfo.txt"
],
"symbols": [
"CPythonNetworkStream::RecvQuestInfoPacket",
"CPythonNetworkStream::RecvQuestConfirmPacket",
"CPythonNetworkStream::RecvScriptPacket",
"CPythonNetworkStream::SendScriptAnswerPacket",
"CPythonNetworkStream::SendScriptButtonPacket",
"CPythonQuest::MakeQuest",
"CPythonQuest::RegisterQuestInstance",
"CPythonQuest::SetQuestClockValue",
"CPythonQuest::GetQuestLastTime",
"CPythonEventManager::RegisterEventSetFromString",
"CPythonEventManager::ProcessEventSet",
"CPythonEventManager::Skip",
"CPythonEventManager::EndEventProcess",
"CPythonMiniMap::AddSignalPoint",
"CPythonMiniMap::ClearAllSignalPoint"
]
},
"implementation": {
"files": [
"project/ui/quest_log.gd",
"project/ui/quest_dialog.gd",
"project/ui/quest_curtain.gd",
"project/ui/char_status_ui.gd",
"project/ui/atlas_ui.gd",
"project/ui/minimap.gd",
"project/atlas_navigation_system.gd",
"project/game_scene.gd",
"extension/src/net/m2_client.cpp",
"extension/src/net/m2_client.h",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_session.h",
"project/biologist_quest_system.gd",
"project/dynamic_bounty_event_system.gd",
"project/event_manager_system.gd",
"project/metin_spawn_event_system.gd"
],
"symbols": [
"EntityStore::apply",
"M2Client::get_quests",
"M2Client::script_button",
"M2Client::script_answer",
"M2Client::quest_input",
"M2Client::quest_confirm",
"M2Client::quest_cancel",
"QuestLog",
"QuestDialog",
"QuestCurtain",
"AtlasUI",
"Minimap",
"GameScene::_on_quest_fade_event",
"GameScene::_on_quest_camera_event"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "GAP",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/quest.quest-event-navigation.md",
"tests": [
"extension/tests/net_entity_test.cpp",
"extension/tests/net_classic_session_test.cpp",
"extension/tests/net_bounds_test.cpp",
"project/quest_test.gd",
"project/quest_event_test.gd",
"project/test_quest_hud_parity.gd",
"project/test_quest_dialog_parity.gd"
],
"last_test_result": "PASS"
},
"remaining": [
"验证 clock_start_ms 与 40250 CTimer::GetCurrentSecond() 的时间基准、后台恢复和过期任务清理,并增加真实任务日志到 UI 行号映射测试",
"补齐真实 server 脚本序列与中断回归;script-button、DONE、ESC/CANCEL answer 语义已闭合",
"验证 RUN_CINEMA 的有效 .msc 资源序列、过场消费者和中断清理;验证地图信号在 Minimap/Atlas 的切图与多点生命周期",
"补齐对话确认超时、fade/camera/cinema 中断、断线和重连清理测试",
"隔离生物学家/悬赏/活动 helper,避免其本地规则冒充 40250 客户端实现"
],
"notes": "已完成任务状态、事件脚本、对话、HUD、地图导航和过场链的逐分支/协议/时序审计;GC_QUEST_INFO END、script-button 任务索引 sentinel、DONE close-only/DoneEvent、ESC/CANCEL answer 分支和 RUN_CINEMA 缺失资源分支已按 40250 修复,其他明确差异继续保持 PARTIAL。",
"fingerprints": {
"reference": "ba46c9195d60a257062b207f81d4fbc730b71d8098d6b2a2a531640f05debe38",
"implementation": "5aa1707764f5a74db510210bfd8803a9e90e0a4d989202795fe506d1f29a8395",
"tests": "a65c31ca4ade9fe4359f1238ed7080a5a50c3a336b75a9d9ee2a1e2f7faeb02a"
}
},
{
"id": "world.dungeon_activity",
"title": "地下城、地图事件、Boss、结果和排行",
"subsystem": "world",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"GameLib/MapManager.cpp",
"GameLib/GameEventManager.cpp",
"GameLib/GameEventManager.h",
"UserInterface/Packet.h",
"UserInterface/PythonNetworkStreamPhaseGame.h",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/PythonPlayer.cpp",
"UserInterface/PythonPlayer.h",
"UserInterface/PythonEventManager.cpp",
"UserInterface/PythonEventManager.h"
],
"symbols": [
"CPythonNetworkStream::RecvDungeon",
"CPythonPlayer::SetDungeonDestinationPosition",
"CPythonPlayer::AlarmHaveToGo",
"CPythonPlayer::Update",
"CPythonEventManager::ProcessEventSet",
"CGameEventManager::ProcessEventScreenWaving"
]
},
"implementation": {
"files": [
"project/dungeon_state.gd",
"project/ui/dungeon_result_ui.gd",
"project/game_scene.gd",
"project/world_boss_system.gd",
"project/dungeon_ranking_system.gd",
"project/spider_dungeon_system.gd",
"project/spider_dungeon_key_system.gd",
"project/flame_dungeon_razador_system.gd",
"project/endless_spire_dungeon_system.gd",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/m2_client.cpp",
"extension/src/net/classic/classic_session.cpp"
],
"symbols": [
"EntityStore::apply",
"M2Client::poll",
"M2Client::request_dungeon",
"ClassicSession::send_dungeon",
"DungeonState::_on_dungeon_event",
"DungeonState::_process",
"DungeonResultUI::show_result",
"GameScene::_on_dungeon_result"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/world.dungeon-activity.md",
"tests": [],
"last_test_result": "PARTIAL"
},
"remaining": [
"接通 alarm_requested 到等价的 compass/mse 资源效果,并验证方向、初次提示和 20 秒重复提示",
"补齐 GC_DUNGEON/DUNGEON_RESULT 的 live 回包、重复/乱序、拒绝、断线、切图和重连测试",
"确认 server cm 到 Godot 世界坐标的轴向、地图基准和到达阈值实机一致",
"把 Boss、排行和具体副本 helper 与 40250 client parity 分开,禁止本地 authority 冒充客户端等价实现"
],
"notes": "已完成 GC_DUNGEON 目标点、结果窗口、地图事件及本地 Boss/排行/副本 helper 的逐分支、协议、资源和时序审计;目标状态算法接近参考,但 compass 副作用未接通,本地 authority 不能视为 40250 客户端实现。",
"fingerprints": {
"reference": "45e83143617b16eda5b4ad7cc38e701fa9590b02a4c36d5adb2dd8a3bae77b7f",
"implementation": "c4885c44a33a107d3095a20b988f26a5a62d50814514e34ffb03c86bfa7a745d",
"tests": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
},
{
"id": "world.fishing_mining",
"title": "钓鱼、采矿、资源采集和水域条件",
"subsystem": "world",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonPlayerInput.cpp",
"UserInterface/InstanceBase.cpp",
"UserInterface/InstanceBaseMotion.cpp",
"UserInterface/Packet.h",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"GameLib/MapOutdoorWater.cpp",
"GameLib/AreaTerrain.cpp"
],
"symbols": [
"CPythonPlayer::NEW_Fishing",
"CPythonPlayer::NEW_CancelFishing",
"CInstanceBase::GetFishingRot",
"CInstanceBase::CanFishing",
"CInstanceBase::StartFishing",
"CInstanceBase::StopFishing",
"CInstanceBase::ReactFishing",
"CInstanceBase::CatchSuccess",
"CInstanceBase::CatchFail",
"CPythonNetworkStream::SendFishingPacket",
"CPythonNetworkStream::RecvFishing",
"CPythonNetworkStream::RecvDigMotionPacket",
"CTerrain::GetWaterHeight",
"CMapOutdoor::RenderWater"
]
},
"implementation": {
"files": [
"project/fishing_mining_system.gd",
"project/fishing_rod_reel_system.gd",
"project/fishing_cooking_camp_system.gd",
"project/mining_refinery_smelt_system.gd",
"project/net_play.gd",
"project/net_world.gd",
"extension/src/metin2_world.cpp",
"extension/src/metin2_world.h",
"extension/src/water_builder.cpp",
"extension/src/water_motion.h",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/m2_client.cpp",
"extension/src/net/classic/classic_session.cpp",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/wire_classic.h"
],
"symbols": [
"M2Client::fishing",
"ClassicSession::send_fishing",
"EntityStore::apply",
"NetPlay::_new_fishing",
"NetPlay::cancel_fishing",
"NetWorld::_on_fishing_event",
"NetWorld::_on_dig_motion",
"Metin2World::get_fishing_rotation",
"Metin2World::can_fishing_position"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/world.fishing-mining.md",
"tests": [],
"last_test_result": "PARTIAL"
},
"remaining": [
"补齐 CanFishing/持竿/持镐动作门控、服务端拒绝和角色消失/重连测试",
"验证 GetFishingRot 的轴向、MapCoord heading 和全地图 water attribute/水高单位",
"让 GC_DIG_MOTION 按 count 复现多个 dig motion,并验证目标缺失、动作完成和中断清理",
"把钓鱼收益、烹饪、营火、采矿、熔炼和宝石脚本标记为 custom/server authority,禁止冒充 40250 client parity"
],
"notes": "已完成钓鱼输入/水面扫描、GC_FISHING、GC_DIG_MOTION、地图水面和当前端生活技能 helper 的逐分支、协议、状态与资源审计;协议骨架和部分算法接近参考,但 live 动作/门控/清理证据不足,本地收益脚本不是 40250 客户端 authority。",
"fingerprints": {
"reference": "bbc26abb3d3a2cdca5abae515be1dbd0a83188cdf44c5e55738e2a3b664a0ee4",
"implementation": "c72913ee472ed61dfa156b67ba7468f8098512972e49c4a1e307aa8740152fca",
"tests": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
},
{
"id": "world.mount_pet_polymorph",
"title": "马匹、宠物、变身和骑乘战斗",
"subsystem": "world",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonPlayer.cpp",
"UserInterface/PythonPlayerSkill.cpp",
"UserInterface/InstanceBase.cpp",
"UserInterface/InstanceBaseMotion.cpp",
"UserInterface/InstanceBase.h",
"GameLib/ActorInstance.cpp",
"UserInterface/PythonNetworkStreamPhaseGameActor.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/Packet.h"
],
"symbols": [
"CPythonNetworkStream::RecvMountPacket",
"CPythonNetworkStream::__RecvCharacterAppendPacket",
"CPythonNetworkStream::RecvCharacterUpdatePacket",
"CInstanceBase::MountHorse",
"CInstanceBase::DismountHorse",
"CInstanceBase::CanAttack",
"CInstanceBase::CanUseSkill",
"CInstanceBase::CanAttackHorseLevel",
"CInstanceBase::IsHoldingPickAxe",
"CInstanceBase::IsPoly"
]
},
"implementation": {
"files": [
"project/horse_system.gd",
"project/horse_combat_system.gd",
"project/horse_level_growth_system.gd",
"project/pet_companion_system.gd",
"project/classic_pet_system.gd",
"project/polymorph_system.gd",
"project/polymorph_marble_system.gd",
"project/mounted_combat_skill_system.gd",
"project/net_play.gd",
"project/net_world.gd",
"project/ui/equip_model.gd",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/m2_client.cpp",
"extension/src/net/classic/classic_parser.cpp",
"extension/src/net/classic/wire_classic.h"
],
"symbols": [
"EntityStore::mut_mount",
"EntityStore::mut_char_update",
"M2Client::poll",
"NetPlay::_refresh_motion_mode",
"NetPlay::_can_attack_horse_level",
"NetWorld::_apply_field_updates",
"EquipModel::_is_mounting",
"EquipModel::_is_poly"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "GAP",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "GAP"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/world.mount-pet-polymorph.md",
"tests": [],
"last_test_result": "PARTIAL"
},
"remaining": [
"补齐 mount_vnum/GC_MOUNT 到实际 horse actor/model attach/detach 的 live 链和位置清理",
"验证骑乘 motion root、300/1200 度旋转速度、马等级攻击门和 weapon mode 的 live 回包时序",
"把变身 affect/shape/装备遮挡接入 native entity 或明确标为缺失,不用本地 helper 代替",
"将宠物、马匹成长/耐力/马上技能和伤害脚本与 40250 client parity 分离,禁止 custom authority 冒充等价实现"
],
"notes": "已完成 mount_vnum/GC_MOUNT、骑乘动作/攻击门、变身和当前端宠物/马匹 helper 的逐分支、协议、资源和时序审计;网络字段与部分门控存在,但实际坐骑模型挂接和变身 native 闭环缺失,本地宠物/成长/技能规则不是 40250 客户端 authority。",
"fingerprints": {
"reference": "deefe3ad38347b0ab207653391cd54b5f06811f6892ee15a8650cc42e8637e98",
"implementation": "333ea5a5c89e7628044ea4b548354e73d27e602b6348eb46cedd24f8a85d7568",
"tests": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
},
{
"id": "render.model_attachment",
"title": "Granny 模型、LOD、骨骼、武器、盾牌和发型挂点",
"subsystem": "render",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"EterGrnLib/ThingInstance.cpp",
"EterGrnLib/ThingInstance.h",
"EterGrnLib/LODController.cpp",
"EterGrnLib/LODController.h",
"EterGrnLib/ModelInstanceUpdate.cpp",
"EterGrnLib/ModelInstance.cpp",
"EterGrnLib/ModelInstanceMotion.cpp",
"EterGrnLib/ModelInstanceModel.cpp",
"EterGrnLib/ModelInstanceRender.cpp",
"EterGrnLib/ModelInstanceCollisionDetection.cpp",
"GameLib/ActorInstanceAttach.cpp",
"GameLib/RaceData.cpp",
"GameLib/RaceData.h",
"GameLib/ItemData.cpp",
"UserInterface/InstanceBase.cpp",
"UserInterface/InstanceBaseRender.cpp"
],
"symbols": []
},
"implementation": {
"files": [
"extension/src/metin2_model.h",
"extension/src/metin2_model.cpp",
"extension/src/metin2_anim.cpp",
"extension/src/gr2_bridge.cpp",
"project/ui/player_view.gd",
"project/ui/equip_model.gd",
"project/ui/remote_player_view.gd",
"project/ui/mob_view.gd",
"project/ui/race_spec.gd"
],
"symbols": []
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "GAP",
"state_transition_order": "PARTIAL",
"constants_units": "GAP",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "STATIC_VERIFIED",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/render.model-attachment.md",
"tests": [
"project/weapon_attach_test.gd",
"project/gpu_lod_attachment_test.gd",
"project/gpu_pose_bounds_test.gd",
"project/race_motion_assembly_test.gd",
"project/equip_model_test.gd",
"project/remote_player_test.gd",
"project/mob_view_test.gd",
"project/rendering_scenario_test.gd",
"project/fly_target_bounds_test.gd",
"project/mob_winding_test.gd",
"project/animation_cache_test.gd"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"统一 40250 Actor LOD 的 500/1500/2500cm 分档、模型队列和缺失资源回退",
"验证 LOD 切换时动作复制、武器/盾牌/发型/盔甲挂点、材质、碰撞和 bounds 同步",
"修复 03150 双手武器 run 动作左手挂点偏移,并建立逐动作挂点矩阵",
"覆盖非连续 LOD、缺骨骼、重复换装、死亡、换图和资源释放;修复 ObjectDB 泄漏与 exit 139",
"补齐变身装备遮挡及 ItemData/RaceData 多模型资源列表等价实现"
],
"notes": "已完成 40250 ThingInstance/LODController/ModelInstance/ActorInstanceAttach/RaceData/ItemData 与当前 GR2/Godot 模型链的首轮逐分支审计;确认默认 LOD 常量、LOD 子模型生命周期和 03150 双手武器姿态存在真实差异,合同保持 PARTIAL,未修改实现。",
"fingerprints": {
"reference": "553e2ce8e6b51e4acf8192cc1f7983865f52a2d68ae2d7c2f1d8aee9fa63f349",
"implementation": "2fedab89ead277ee74f94bda5ee2e5bdc4810fe3a2897f250e5911c0be81df04",
"tests": "74ebcaa45ad2a714c654a1e76045df3594d858b3c83f265789e39cf6d8bba1e1"
}
},
{
"id": "render.effect_particle_motion",
"title": "MDE/MSE、粒子、动作事件挂点和回收",
"subsystem": "render",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"GameLib/RaceMotionData.cpp",
"GameLib/RaceMotionDataEvent.h",
"GameLib/ActorInstanceMotionEvent.cpp",
"EffectLib/EffectManager.cpp",
"EffectLib/EffectInstance.cpp",
"EffectLib/ParticleInstance.cpp",
"UserInterface/PythonEffectModule.cpp"
],
"symbols": []
},
"implementation": {
"files": [
"formats/msa.cpp",
"formats/msa.h",
"extension/src/metin2_anim.cpp",
"project/fx/effect_player.gd",
"project/fx/mde.gd",
"project/fx/mse.gd",
"project/fx/skill_fx.gd",
"project/fx/effect_registry.gd",
"project/fx/motion_effect_anchor.gd",
"project/fx/target_effect_anchor.gd",
"project/game_scene.gd",
"project/ui/player_view.gd",
"project/ui/remote_player_view.gd",
"project/ui/mob_view.gd",
"project/net_world.gd"
],
"symbols": [
"fmt::MotionEvent",
"Metin2AnimPlayer::get_events",
"Metin2AnimPlayer::emit_motion_event",
"GameScene::_apply_motion_warp"
]
},
"equivalence": {},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/render.effect-particle-motion.md",
"tests": [
"project/fx_test.gd",
"project/effect_playback_test.gd",
"project/motion_effect_anchor_test.gd",
"project/target_effect_test.gd",
"project/skill_fx_test.gd",
"project/effect_color_test.gd",
"project/effect_emission_test.gd",
"project/effect_faces_test.gd",
"project/effect_lie_test.gd",
"project/effect_random_rotation_test.gd",
"project/effect_render_regression_test.gd",
"project/effect_rotation_test.gd",
"project/effect_scale_test.gd",
"project/effect_space_test.gd",
"project/effect_surface_test.gd",
"project/effect_texture_animation_test.gd",
"project/motion_event_payload_test.gd",
"project/motion_event_visibility_test.gd",
"project/net_world_motion_event_test.gd",
"project/motion_event_warp_test.gd"
],
"last_test_result": "PASS"
},
"remaining": [
"type 4/6 的生产分支和专用字段消费;type 9 WARP 已接入但远端目标生命周期仍待 live 验证",
"SPECIAL_ATTACKING splash、FLY .fly、WARP 的真实服务端状态包和跨图清理",
"逐帧、loop、缺字段、删除/死亡/重连边界测试",
"motion_effect_anchor_test exit 139 和 ObjectDB 泄漏"
],
"notes": "基础 MDE/MSE/粒子/挂点已覆盖,但 40250 ActorInstanceMotionEvent 的完整分支和所有 Actor 消费链仍不等价;保持 PARTIAL。",
"fingerprints": {
"reference": "0f027842dc3a7991870c155a9a9453babb4c0f8fc55ac2c4c7f0bb75aaa445c8",
"implementation": "1e7106b1936bf6ac79849544cc8dd473bb0a421b3666d98e99721f0f84fc4825",
"tests": "cb6e83d8a0c3e1696d6638eb582a6c95f48fc3c19e3fbfb9c285ba5906f3aa52"
}
},
{
"id": "world.terrain_water_collision",
"title": "高度、属性阻挡、水面、桥梁、拾取射线和地面贴合",
"subsystem": "world",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"GameLib/MapOutdoor.cpp",
"GameLib/AreaTerrain.cpp",
"GameLib/MapOutdoorWater.cpp",
"GameLib/TerrainDecal.cpp",
"EterLib/AttributeInstance.cpp",
"PRTerrainLib/Terrain.cpp"
],
"symbols": [
"CMapOutdoor::GetTerrainHeight",
"CMapOutdoor::GetHeight",
"CMapOutdoor::GetPickingPointWithRay",
"CMapOutdoor::GetPickingPointWithRayOnlyTerrain",
"CMapOutdoor::isAttrOn",
"CMapOutdoor::GetAttr",
"CMapOutdoor::GetWaterHeight",
"CTerrain::GetHeight",
"CTerrain::isAttrOn",
"CTerrain::GetWaterHeight",
"CAttributeInstance::GetHeight",
"CAttributeInstance::Picking",
"CMapOutdoor::RenderWater"
]
},
"implementation": {
"files": [
"extension/src/metin2_world.cpp",
"formats/area_data.cpp",
"formats/attribute_data.cpp",
"extension/src/water_builder.cpp",
"extension/src/water_depth.h",
"extension/src/water_motion.h",
"project/player_controller.gd",
"project/net_world.gd",
"project/hit_collision.gd",
"project/test_bridge_height_parity.gd",
"project/test_all_map_height_probe.gd",
"project/water_reference_test.gd",
"project/fishing_water_test.gd"
],
"symbols": [
"fmt::terrain_height_at",
"fmt::load_height_map",
"fmt::load_attr_map",
"fmt::load_water_map",
"Metin2World::build_chunk",
"Metin2World::sample_height",
"Metin2World::sample_attribute",
"Metin2World::get_fishing_rotation",
"PlayerController::_ray_ground",
"NetWorld::_ground_world",
"NetWorld::_blocked"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/world.terrain-water-collision.md",
"tests": [
"build/formats/formats_map_test",
"project/test_bridge_height_parity.gd",
"project/test_all_map_height_probe.gd",
"project/water_reference_test.gd",
"project/fishing_water_test.gd",
"project/test_click_target_effect_parity.gd",
"project/player_move_test.gd",
"project/test_skill_ground_fix.gd"
],
"last_test_result": "PASS"
},
"remaining": [
"静态 .mdatr 对象与地形统一 ray picking,并核对对象/地形命中优先级",
"实现 water id 到具体 layer height 的运行时查询并接入钓鱼目标 Z",
"补齐 CTerrain::isAttrOn 的高半字节属性语义和调用方矩阵",
"streaming 未加载/跨区块/地图卸载期间的高度、碰撞、水状态和重试时序",
"HeightMapShape3D、.mdatr 物理体与逻辑 ATTRIBUTE_BLOCK 的边缘/斜坡/清理一致性"
],
"notes": "已完成地形/水面/碰撞/拾取首轮逐分支审计;高度、桥面和水面 happy path 已验证,但对象 ray picking、水层高度、高半字节属性和 streaming/清理边界保持 PARTIAL。",
"fingerprints": {
"reference": "46da799d71b3ad4ec459ebaa5364dac82a1cd503a30c55692ccadaf183ade1cc",
"implementation": "0492040f92b685f42e6d553cb50bdf6e67ff22bf757a886f22b9ee230df6912b",
"tests": "a2a38be2cfb31518ed7f30aab7a2aeb324b71dce353271fa4ea1f330d21195f3"
}
},
{
"id": "world.environment_weather",
"title": "天空、环境、光照、昼夜、雪和天气",
"subsystem": "world",
"priority": "P2",
"status": "PARTIAL",
"reference": {
"files": [
"../Client/Eternexus/root/game.py",
"../Client/Eternexus/root/servercommandparser.py",
"GameLib/MapOutdoorRender.cpp",
"GameLib/MapOutdoorRenderHTP.cpp",
"GameLib/MapOutdoorRenderSTP.cpp",
"GameLib/SnowEnvironment.cpp",
"GameLib/SnowParticle.cpp",
"GameLib/SnowParticle.h",
"EterLib/EnvironmentMap.cpp",
"EterLib/EnvironmentMap.h"
],
"symbols": [
"MapOutdoor::OnBeginEnvironment",
"MapOutdoor::OnSetEnvironmentDataPtr",
"MapOutdoor::SetEnvironmentSkyBox",
"MapOutdoor::SetEnvironmentLensFlare",
"MapOutdoor::RenderSky",
"MapOutdoor::RenderCloud",
"CSnowEnvironment",
"CSnowParticle"
]
},
"implementation": {
"files": [
"extension/src/environment_builder.cpp",
"extension/src/environment_builder.h",
"extension/src/metin2_world.cpp",
"extension/src/net/entity_store.cpp",
"extension/src/net/entity_store.h",
"extension/src/net/m2_client.cpp",
"formats/environment.cpp",
"formats/environment.h",
"project/game_scene.gd",
"project/weather_daynight_system.gd",
"project/fx/weather.gd"
],
"symbols": [
"mtgodot::apply_environment",
"Metin2World::load_map",
"EntityStore::apply_server_command",
"M2Client::weather_changed",
"GameScene::_build_world",
"GameScene::_on_weather_changed",
"GameScene::_apply_server_weather",
"Weather",
"WeatherDayNightSystem"
]
},
"equivalence": {
"environment_parse_load_cleanup": "PARTIAL",
"background_character_lighting": "PARTIAL",
"fog_near_far_color_density": "PARTIAL",
"sky_gradient_faces_cloud_transition": "PARTIAL",
"screen_filter": "GAP",
"lens_flare": "GAP",
"wind_to_forest": "GAP",
"snow_particle_pool_render_order": "PARTIAL",
"server_xmas_snow_command": "PARTIAL",
"day_night_weather_scene_integration": "PARTIAL",
"reload_pause_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/world.environment-weather.md",
"tests": [
"project/environment_test.gd",
"project/skybox_test.gd",
"project/p9_test.gd",
"project/test_weather_daynight_parity.gd",
"extension/tests/net_entity_test.cpp",
"project/gamescene_test.gd",
"project/netbridge_test.gd"
],
"last_test_result": "PARTIAL: msenv/skybox/weather tests, xmas_snow native command, GameScene map filtering and M2Client signal pass; SnowEnvironment pool, DayMode, filter/lens flare/wind and linear fog remain open"
},
"remaining": [
"实现或明确 screen filter、lens flare 和 SpeedTree/树木风更新,不以 metadata 代替运行时效果",
"修正/验证 40250 线性 fog near/far/color 与 HTP/STP/terrain 的参数和时序",
"补齐 DayMode/PRESERVE_DayMode 正式环境切换;WeatherDayNightSystem 的自动地图预设继续隔离,不能替代服务器 xmas_snow 命令",
"对天空 transition、云 plane、SnowEnvironment 池和真实渲染顺序增加参数/screenshot oracle",
"增加重复加载、快速切图、暂停恢复、资源缺失和环境/粒子清理测试"
],
"notes": "已完成 .msenv、天空/云、双方向光、fog、天气粒子、服务器 xmas_snow 命令和正式场景过滤的逐分支审计;命令链与四张排除地图已有回归,但 SnowEnvironment 池、DayMode、filter/lens flare/wind、线性 fog 仍与 40250 不一致或缺失,故保持 PARTIAL。",
"fingerprints": {
"reference": "0a076b8a9779c989184c20ef617cddf5538720e8ac7e2e0773d63af3d72e6eb0",
"implementation": "dfc0fe4b13645b06daeea26f517d813a8c8ebb4cfcb2db19ebe2ee7e8f573c7c",
"tests": "05fd8d3202d9774f7043311e8615e1210a09cc579f1346eb2fb9ebbfec339b5b"
}
},
{
"id": "ui.input_camera_mobile",
"title": "鼠标、相机、光标、IME、触屏和移动端窗口",
"subsystem": "ui",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonApplicationCamera.cpp",
"UserInterface/PythonApplicationCursor.cpp",
"UserInterface/PythonPlayerInputMouse.cpp",
"UserInterface/PythonIME.cpp",
"EterLib/Input.cpp"
],
"symbols": [
"CPythonApplicationCamera::__UpdateCamera",
"CPythonApplicationCamera::RotateCamera",
"CPythonApplicationCamera::PitchCamera",
"CPythonApplicationCamera::ZoomCamera",
"CPythonPlayerInputKeyboard::NEW_SetMultiDirKeyState",
"CPythonPlayerInputMouse::NEW_SetMouseState",
"CPythonPlayerInputMouse::NEW_SetMouseSmartState",
"CPythonApplicationCursor::SetCursorNum",
"CPythonIME::OnUpdate"
]
},
"implementation": {
"files": [
"project/game_camera.gd",
"project/ui/mouse_controller.gd",
"project/ui/cursor_manager.gd",
"project/ui/mobile/mobile_window_host.gd",
"project/ui/mobile/mobile_input_overlay.gd",
"project/player_controller.gd"
],
"symbols": [
"GameCamera.set_event_camera",
"GameCamera._process",
"PlayerController._wasd",
"PlayerController._input",
"PlayerController._unhandled_input",
"MouseController.cancel_all",
"CursorManager.set_cursor",
"MobileUIRoot"
]
},
"equivalence": {
"keyboard_direction_state": "PARTIAL",
"mouse_dispatch_and_smart_priority": "PARTIAL",
"camera_normal_and_event_modes": "PARTIAL",
"cursor_state_and_visibility": "PARTIAL",
"ime": "GAP",
"mobile_touch_window_adaptation": "PASS_WITH_ADAPTATION",
"focus_pause_exit_cleanup": "PARTIAL"
},
"platform_adaptations": [
"Godot mobile touch/joystick/pinch/landscape UI is a platform adaptation and has no direct Windows 40250 branch to compare 1:1."
],
"evidence": {
"contract": "audit/contracts/ui.input-camera-mobile.md",
"tests": [
"project/game_camera_test.gd",
"project/mouse_controller_test.gd",
"project/input_key_test.gd",
"project/mobile_gesture_test.gd",
"project/mobile_ui_test.gd",
"project/mobile_window_host_test.gd"
],
"last_test_result": "PARTIAL: game_camera_test 3 failures; other five tests passed with leak warnings"
},
"remaining": [
"统一 Camera 默认值、比例缩放、RESTORE_CAMERA 快照和事件相机锁定状态",
"补齐 NEW_SetMouseState 全按钮/智能点击/中键/Ctrl/角色菜单分派矩阵",
"补充连续光标、硬件光标显隐、IME 回调和生产场景组合清理测试",
"补齐输入失焦、暂停、切图、重连和退出时的统一状态清理证据"
],
"notes": "已完成首轮逐分支审计;移动端基础适配测试通过,但相机默认/恢复、鼠标统一分派、光标状态和 IME 仍未达到 40250 等价。",
"fingerprints": {
"reference": "11181965574e3b0c3e235b1403e17494f8fc1b8777a2b18b955f53a84e0bae54",
"implementation": "d67243d6e4c881a79c37af554eeaeca5374c3a73caf7d6e96f8e8abdcabce9bf",
"tests": "eb158440ac1fcfce57259aad60a090493d2cebaeab7e380c3513cb34563a90fd"
}
},
{
"id": "ui.hud_target_minimap_status",
"title": "角色状态、血条、目标框、小地图、文本尾标和快捷栏",
"subsystem": "ui",
"priority": "P1",
"status": "PARTIAL",
"reference": {
"files": [
"UserInterface/PythonTextTail.cpp",
"UserInterface/PythonTextTail.h",
"UserInterface/PythonMiniMap.cpp",
"UserInterface/PythonMiniMap.h",
"UserInterface/PythonPlayer.cpp",
"UserInterface/PythonPlayer.h",
"UserInterface/PythonApplication.cpp",
"UserInterface/PythonApplication.h",
"UserInterface/PythonPlayerEventHandler.cpp",
"UserInterface/InstanceBase.cpp",
"UserInterface/InstanceBaseEffect.cpp",
"UserInterface/PythonNetworkStreamPhaseGame.cpp",
"UserInterface/uitaskbar.py",
"UserInterface/uicharacter.py",
"UserInterface/uitarget.py"
],
"symbols": [
"CPythonTextTail::UpdateAllTextTail",
"CPythonTextTail::UpdateShowingTextTail",
"CPythonTextTail::ArrangeTextTail",
"CPythonMiniMap::SetScale",
"CPythonMiniMap::ScaleUp",
"CPythonMiniMap::ScaleDown",
"CPythonPlayer::SetStatus",
"CPythonPlayer::RequestUseLocalQuickSlot",
"CPythonNetworkStream::RecvTargetPacket"
]
},
"implementation": {
"files": [
"project/hud.gd",
"project/net_play.gd",
"project/net_world.gd",
"project/ui/char_status_ui.gd",
"project/ui/target_ui.gd",
"project/target_board.gd",
"project/ui/minimap.gd",
"project/text_tail.gd",
"project/text_tail_arrange.gd",
"project/chat_tail.gd",
"project/ui/ground_items.gd",
"project/ui/quickbar.gd"
],
"symbols": [
"NetPlay::_on_points",
"NetPlay::_on_target_info",
"NetWorld::_apply_text_tail_to",
"NetWorld::_update_hp_bars",
"Minimap::_draw_map",
"Quickbar::_on_points_changed",
"CharStatusUI::_refresh_status"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "GAP",
"state_transition_order": "PARTIAL",
"constants_units": "GAP",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "PARTIAL",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/ui.hud-target-minimap-status.md",
"tests": [
"project/test_target_board_parity.gd",
"project/target_board_test.gd",
"project/test_minimap_radar_parity.gd",
"project/p9_test.gd",
"project/text_tail_test.gd",
"project/text_tail_arrange_test.gd",
"project/chat_tail_test.gd",
"project/chat_test.gd",
"project/name_show_test.gd",
"project/char_status_ui_test.gd",
"project/test_taskbar_quickslot_parity.gd",
"project/taskbar_dock_test.gd"
],
"last_test_result": "PARTIAL"
},
"remaining": [
"在正式 GameScene 创建并注入唯一 TargetUI,移除/隐藏简化目标条并补生产链回归",
"只保留一套生产小地图,统一 40250 的 scale、terrain-cell 单位、按钮倍率和切换行为",
"集中 points/quickslot/target/party/delete/world-reset 的 UI owner 并覆盖重复创建、死亡、换图、重连、退出清理",
"逐帧对照 TextTail UpdateAll/Show/UpdateShowing/Arrange,消除 CanPickInstance、2Hz 和字体度量近似",
"补真实资源/网络包驱动的 HUD/marker 测试并修复 Quickbar ObjectDB/RID/resource cleanup"
],
"notes": "已完成 PythonTextTail/PythonMiniMap/PythonPlayer/TargetBoard/TaskBar 与当前 GameScene/UI 链的首轮逐分支审计;确认 TargetUI 未在正式 GameScene 注入、HUD 与新 Minimap 重复创建、Minimap scale 常量与 40250 不同,合同保持 PARTIAL,未修改实现。",
"fingerprints": {
"reference": "a948059544c578dfb6f67d4e64ed41322095fb5734835aaa60aa4dd53567d152",
"implementation": "6b31f5832b14c75d5210ed1bf312c88f550c8d03eadbcf5b9f57b1983c4ec2c4",
"tests": "fd53e0750b11c9d06bfc1613b53ed864f712732719c2c495a19b30fed6663c9c"
}
},
{
"id": "resource.pack_locale_proto",
"title": "EterPack、资源解析、locale、proto、MSA/MSM 和格式转换",
"subsystem": "resource",
"priority": "P0",
"status": "PARTIAL",
"reference": {
"files": [
"EterPack/EterPackManager.cpp",
"EterPack/EterPackCursor.cpp",
"EterPack/EterPack.cpp",
"EterPack/EterPackPolicy_CSHybridCrypt.cpp",
"EterPack/StdAfx.cpp",
"EterLocale/StringCodec_Vietnamese.cpp",
"EterLocale/Japanese.cpp",
"EterLocale/Arabic.cpp",
"EterLocale/StringCodec.cpp",
"EterLocale/StdAfx.cpp",
"UserInterface/PythonPackModule.cpp",
"GameLib/ItemData.cpp",
"GameLib/ItemManager.cpp",
"UserInterface/PythonNonPlayer.cpp",
"GameLib/RaceDataFile.cpp"
],
"symbols": [
"CEterPackManager::Get",
"CEterPackCursor::Read",
"packGet",
"CItemManager::LoadItemTable",
"CPythonNonPlayer::LoadNonPlayerData",
"CRaceData::LoadRaceData"
]
},
"implementation": {
"files": [
"extension/src/pack/pack_mount.cpp",
"extension/src/asset_io.cpp",
"project/locale.gd",
"extension/src/proto/proto.cpp",
"formats/texture_set.cpp",
"formats/terrain_files.cpp",
"formats/msm.cpp",
"formats/environment.cpp",
"formats/asset_resolver.cpp",
"formats/attribute_data.cpp",
"formats/property.cpp",
"formats/terrain_mesh.cpp",
"formats/spt.cpp",
"formats/m2_tokvec.cpp",
"formats/map_setting.cpp",
"formats/splat.cpp",
"formats/msa.cpp",
"formats/combo_table.cpp",
"formats/textscript.cpp",
"formats/area_data.cpp",
"formats/m2_coord.cpp",
"formats/tests/attribute_data_test.cpp",
"formats/tests/msm_test.cpp",
"formats/tests/combo_table_test.cpp",
"formats/tests/map_formats_test.cpp",
"formats/tests/msa_test.cpp",
"extension/tests/pack_roundtrip_test.cpp",
"extension/tests/proto_test.cpp",
"extension/tests/proto_item_layout_test.cpp",
"project/test_chinese_locale.gd",
"project/skill_test.gd"
],
"symbols": [
"AssetRoot",
"AssetPack",
"AssetResolver",
"PackMount",
"EterPack",
"Metin2Proto",
"Locale",
"MsaMotion",
"ComboTable"
]
},
"equivalence": {
"preconditions": "PARTIAL",
"branch_structure": "PARTIAL",
"algorithms_formulas": "PARTIAL",
"state_transition_order": "PARTIAL",
"constants_units": "PARTIAL",
"timing_event_sources": "PARTIAL",
"resource_data_sources": "PARTIAL",
"protocol_side_effects": "MAPPED",
"interruption_failure_cleanup": "PARTIAL"
},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/resource.pack-locale-proto.md",
"tests": [
"extension/tests/pack_roundtrip_test.cpp",
"extension/tests/proto_test.cpp",
"extension/tests/proto_item_layout_test.cpp",
"formats/tests/msm_test.cpp",
"formats/tests/msa_test.cpp",
"formats/tests/combo_table_test.cpp",
"formats/tests/map_formats_test.cpp",
"project/test_chinese_locale.gd",
"project/skill_test.gd"
],
"last_test_result": "PASS"
},
"remaining": [
"把 pack/loose/AssetResolver/Locale/Proto 统一到实际运行时虚拟读取入口",
"确认 40250 EterPack 原格式、搜索模式、overlay、CRC/cache 与当前格式替换的边界",
"CP125x/Vietnamese/Arabic/Japanese 等 locale 编码和坏字节行为",
"proto/MSA/MSM/技能/模型在 pack-only/PCK-only/缺文件/坏文件下的错误传播",
"切换语言、重载资源、patch 覆盖和资源缓存失效的自动测试"
],
"notes": "proto 和格式解析已核对并有测试;主资源入口、EterPack 原格式、locale 编码和失败回退存在材料差异,保持 PARTIAL。",
"fingerprints": {
"reference": "772c0563544d7e07bc38ae2c2a3684b5f1a248d22200dfaa108b2967e26e3ca8",
"implementation": "313cb99fea6b2d3c7cdec918aad2c328d81b42fd75ee582bf301241b6507621d",
"tests": "40416fb46b72679bfba5de491b7945b0cf95d08f524c9786589625f3641e6da3"
}
},
{
"id": "audio.bgm_ambience_3d",
"title": "BGM、区域环境音、角色音效、3D 监听器和前后台恢复",
"subsystem": "audio",
"priority": "P2",
"status": "PARTIAL",
"reference": {
"files": [
"MilesLib/SoundManager3D.cpp",
"MilesLib/SoundManager.cpp",
"MilesLib/SoundManager2D.cpp",
"MilesLib/SoundManagerStream.cpp",
"UserInterface/PythonSoundManagerModule.cpp",
"GameLib/MapOutdoor.cpp",
"GameLib/Area.cpp",
"GameLib/Area.h"
],
"symbols": [
"CSoundManager::Create",
"CSoundManager::Update",
"CSoundManager::PlaySound3D",
"CSoundManager::PlayAmbienceSound3D",
"CSoundManager::PlayCharacterSound3D",
"CSoundManager::FadeInMusic",
"CSoundManager::FadeOutMusic",
"CSoundManager::SaveVolume",
"CSoundManager::RestoreVolume",
"CArea::TAmbienceInstance::UpdateLoopSound",
"CArea::TAmbienceInstance::UpdateOnceSound",
"CArea::TAmbienceInstance::UpdateStepSound"
]
},
"implementation": {
"files": [
"project/audio.gd",
"project/bgm_director.gd",
"project/app_lifecycle.gd",
"project/game_scene.gd",
"formats/environment.cpp",
"formats/area_data.cpp",
"extension/src/metin2_world.cpp"
],
"symbols": [
"Audio.play_bgm",
"Audio._play_3d",
"Audio.update_ambience_sources",
"Audio.play_character_sound_3d",
"Audio.save_volume",
"Audio.restore_volume",
"Audio.shutdown",
"BgmDirector._select",
"AppLifecycle._enter_background",
"GameScene._exit_tree",
"GameScene._process"
]
},
"equivalence": {
"sound_manager_lifecycle": "PARTIAL",
"bgm_state_machine": "PARTIAL",
"volume_curve": "PASS_WITH_ADAPTATION",
"listener_and_3d_scale": "PARTIAL",
"character_motion_sound": "PARTIAL",
"area_ambience": "PARTIAL",
"background_save_restore": "PARTIAL"
},
"platform_adaptations": [
"Godot AudioStreamPlayer/AudioStreamPlayer3D replaces Miles provider/driver; exact attenuation and hardware-provider behavior is a platform adaptation."
],
"evidence": {
"contract": "audit/contracts/audio.bgm-ambience-3d.md",
"tests": [
"project/audio_miles_test.gd",
"project/ambience_test.gd",
"project/bgm_test.gd",
"project/test_bgm_jukebox_parity.gd",
"project/app_flow_lifecycle_test.gd",
"project/system_option_ui_test.gd",
"project/rendering_scenario_test.gd"
],
"last_test_result": "PARTIAL: focused BGM/SFX background mute-restore and Debug exit cleanup pass; coverage does not prove Miles instance pool or multi-instance music state"
},
"remaining": [
"验证真实 3D 播放实例不会绕过 master SFX,并补设备/实例路径的后台静音回归",
"验证 BGM 多实例淡入/限幅淡出/全量淡出/加载失败回滚状态机",
"验证 3D attenuation、实例池耗尽、远端动作音效和完整销毁顺序",
"验证流式地图快速切换、环境声来源删除和退出时无悬挂播放器"
],
"notes": "已完成首轮逐分支审计;基础音量/监听器/环境声/动作音效路径存在,后台 BGM/SFX 同时静音恢复与退出 Audio 缓存清理已有回归,但 Miles 实例池和 BGM 逐帧状态机仍不等价。",
"fingerprints": {
"reference": "2d3182776cf583a5e474701489a417e87ff2f8d7952a4ce03a4523dd570528cd",
"implementation": "f4c3fed1f7cbf08c1032c536927a689a9a1b71b1c431de2afb3c0041ee577315",
"tests": "a7ca9e4555b71f92776b56669a0b74e443dc2aaab78a2f208c86f9fc4bc75bfc"
}
},
{
"id": "lifecycle.platform_release",
"title": "Windows/桌面窗口、导出、资源自包含、崩溃和发布门禁",
"subsystem": "lifecycle",
"priority": "P2",
"status": "PARTIAL",
"reference": {
"files": [
"EterLib/MSApplication.cpp",
"EterLib/MSWindow.cpp",
"UserInterface/PythonApplication.cpp",
"UserInterface/PythonApplicationProcedure.cpp",
"UserInterface/CheckLatestFiles.cpp",
"EterPack/EterPackManager.cpp"
],
"symbols": [
"CMSApplication::MessageLoop",
"CMSApplication::WindowProcedure",
"CMSWindow::Create",
"CMSWindow::OnSize",
"CPythonApplication::Process",
"CPythonApplication::WindowProcedure",
"CheckLatestFilesEntry"
]
},
"implementation": {
"files": [
"build.sh",
"build-macos-client.sh",
"build-android.sh",
"export-android.sh",
"project/export_presets.cfg",
"project/client_main.gd",
"project/app_flow.gd",
"project/app_lifecycle.gd",
"project/asset_root.gd",
"project/asset_pack.gd"
],
"symbols": [
"AppFlow.start",
"AppFlow._goto_game",
"AppLifecycle._notification",
"AssetRoot.path",
"AssetPack.ensure"
]
},
"equivalence": {
"windows_window_and_message_loop": "GAP",
"application_update_render_exit_order": "PARTIAL",
"focus_fullscreen_and_close": "PARTIAL",
"resource_self_containment": "PARTIAL",
"latest_file_crc_update_check": "GAP",
"exception_and_crash_handling": "GAP",
"macos_android_export": "PASS_WITH_ADAPTATION",
"windows_export": "GAP"
},
"platform_adaptations": [
"Godot MainLoop/Window and platform export replace the reference Win32 CMSApplication/CMSWindow; this is acceptable only as an explicit platform adaptation, not as Windows 1:1 parity."
],
"evidence": {
"contract": "audit/contracts/lifecycle.platform-release.md",
"tests": [
"project/app_flow_lifecycle_test.gd",
"project/bgm_test.gd",
"project/dds_lifecycle_test.gd",
"godot --headless --path project --editor --quit",
"godot --headless --path project --export-release macOS /tmp/mtgodot-platform-audit.app"
],
"last_test_result": "PARTIAL: focus activation/audio save-restore, AppFlow, DDS, editor validation and macOS export passed; Windows export/window/CRC/crash paths absent"
},
"remaining": [
"增加 Windows 导出预设、GDExtension 构建和整包启动/退出验证",
"补窗口 resize/全屏/关闭/IME/native input 的平台测试;焦点激活音量路径已有回归",
"补资源版本/CRC/缺包/坏包/路径大小写门禁",
"补异常处理、crash report、正常退出和 native 资源释放证据"
],
"notes": "已完成首轮逐分支审计;AppFlow、AssetPack、焦点激活音量保存恢复和 macOS/Android 适配存在,但 Windows 原生窗口/导出、全屏与 resize、CRC 更新检查、Eter 异常处理和完整发布门禁仍缺失。",
"fingerprints": {
"reference": "ddccccedfb929112d89c6c14b3746569a64493245a0e8b70145df3979dfd611d",
"implementation": "84417dac55504e5f626731fd2d3c78decec8808187fa70b427fdf83f66adf040",
"tests": "10987358f838e043daaed30ccb4639f738a6bbd19522783091a9916fbb5e2bc1"
}
},
{
"id": "extension.current_only_features",
"title": "没有 40250 参考实现的新增玩法和平台扩展",
"subsystem": "extension",
"priority": "P3",
"status": "EXCLUDED",
"reference": {
"files": [],
"symbols": []
},
"implementation": {
"files": [
"project/offline_bazaar_market_system.gd",
"project/mail_system.gd",
"project/dynamic_bounty_event_system.gd",
"project/dragon_soul_system.gd",
"project/solo_guild_domain_system.gd"
],
"symbols": []
},
"equivalence": {},
"platform_adaptations": [],
"evidence": {
"contract": "audit/contracts/function-inventory.md",
"tests": [],
"last_test_result": "NOT_RUN"
},
"remaining": [],
"notes": "完整功能清单首批合同;尚未完成 40250 逐分支等价审计。",
"exclusion_reason": "在指定 40250 ClientVS22 source 中没有可达对应行为;记录该当前端扩展以防止漏审,但不计入 40250 等价率。",
"fingerprints": {
"reference": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"implementation": "fdaf946a6bffa5e914dfe7f40c2bbd42dc00e64d43e60231e9290021f14cbba2",
"tests": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
}
]
}