- No repo-level CI/test runner is configured: no
.github/workflows, nopytest/toxconfig, noMakefile, andrequirements.txtis empty. Verify with targeted scripts instead of guessing a global command. pyproject.tomlonly configures formatting. Preserve Black atline-length = 120, keep single quotes if already present (skip-string-normalization = true), and keepisort's one-import-per-line style (force_single_line = true).pyrightconfig.jsononly setsstubPath = ./stubsand suppresses missing module source noise. Usepyrightonly if it is installed in the environment.- README explicitly targets Python 3.13.0 32-bit for injected/runtime work. Do not casually switch interpreter versions when debugging launcher or injection issues.
Py4GWCoreLib/__init__.pyis a broad convenience facade, not a minimal import surface: it manually appends systemsite-packages, re-exports most high-level modules, and redirectssys.stdout/sys.stderrinto the Py4GW console. Avoid treatingimport Py4GWCoreLibas a neutral import when debugging startup/import side effects.
docs/Py4GW_Conceptual_Model.mdis the canonical architecture/source-of-truth document for project layers and terminology.docs/MCP_bridge.mdis the MCP-facing bridge planning summary; use it for bridge/MCP modeling, not as the primary architecture source.BridgeRuntime/README.mdis the operator/runtime usage reference for daemon + injected bridge client + CLI.docs/Py4GW_Model_Features_Detail.txtis a derived plain-text export for quick scanning, not a separate authority.docs/widget_manager_and_catalog.mdis the highest-value reference before changing widget discovery, widget metadata defaults,WidgetHandler, orWidgetCatalogbehavior.
- WASM-first workflow (do this by default). Reverse-engineer on
/Gw.wasmfirst, then map the confirmed result to/Gw.exe. The WASM retains full debug symbols (CCharAgent::GetConsiderColor,FrameCreate,CtlTextMl::Markup, …), so behaviour, control flow, struct fields, and call chains are far faster and less error-prone to read there. The EXE is stripped (FUN_xxxxxxxx) — only enter it at the end, to resolve the concrete address the injector needs. Reading architecture in the EXE first is slow and mistake-prone. Watch for genuine ABI differences (WASMcall_indirecttable indices vs. x86 real pointers; possibleColor4b/struct channel-order repacks) — the architecture transfers, but re-confirm low-level calling/ABI details on the EXE. When calling Ghidra MCP tools, always pass the explicitprogrampath (the project has multiple same-namedGw.exeimages; a name-omitted call silently hits the wrong one). Seedocs/RE/CPP_WASM_MAPPING.mdfor the translation procedure. - Start with
docs/RE/reverse_engineering_reference.md— the comprehensive library reference. Covers the three-layer architecture (Pythonnative_src, C++ GWCA, Ghidra), key function catalogs with EXE↔WASM↔CPP mappings, bridging techniques, UI message dispatch architecture, and workflows for adding new functions. docs/RE/CPP_WASM_MAPPING.md— the full CPP↔WASM↔EXE translation procedure with worked examples and pitfall notes.docs/RE/rosetta_stone.txt— GwA2 (AutoIt) to Py4GW function mapping reference.docs/RE/gw_combat_ai_reverse_engineering.md— combat AI RE analysis.docs/RE/native_gw_ui_function_catalog.json— catalog of native GW UI functions with addresses.docs/RE/native_gw_window_creation_investigation.md— window proc creation RE.docs/RE/native_ui_title_and_encoded_string_reference.md— UI title and encoding reference.docs/agent_name_tag_color.md— feature/usage guide forPyAgentTagColor(recolor agent name tags natively): Python API, ARGB color format, examples, gotchas. SHIPPED & validated in-client.docs/RE/name_tag_color_reverse_engineering.md— the RE behind it: agent/item name-tag color pipeline, theGetConsiderColorresolver detour recipe/ABI (hook the resolverFUN_007f02e0; the wrapperFUN_007d9cf0is only an anchor), allegiance→ARGB table, and item-rarity markup. Native module:Py4GW/src/py_agent_tag_color.cpp. In-client test harness:tests/name_tag_color/name_tag_color_test.py.
| Layer | Path | Key Files |
|---|---|---|
| C++ (GWCA) | C:\Users\Apo\Py4GW\vendor\gwca\Source\ |
AgentMgr.cpp, UIMgr.cpp, GameThreadMgr.cpp |
| C++ (GWCA headers) | C:\Users\Apo\Py4GW\vendor\gwca\Include\GWCA\ |
Managers/AgentMgr.h, Utilities/Scanner.h |
| Python native | Py4GWCoreLib\native_src\ |
methods/PlayerMethods.py, internals/native_function.py |
| Python Scanner | Py4GWCoreLib\Scanner.py |
FindAssertion, FindInRange, ToFunctionStart |
| Ghidra EXE | /Gw.exe(Symbols) via MCP |
18,017 functions, x86:LE:32, base 0x00400000 |
| Ghidra WASM | /Gw.wasm via MCP |
18,004 functions, Wasm:LE:32, base ram:80000000 |
| GWCA Name | WASM Symbol | EXE Address |
|---|---|---|
DoWorldActon_Func |
CoreActionExecuteWorldAction |
0x0050e5e0 |
CallTarget_Func |
CharCliPlayerOrderAlertSimple |
0x00917740 |
ChangeTarget_Func |
IAgentView::SetSelections |
0x007e0f60 |
MoveTo_Func |
IUi::Game::Walk* |
0x00534fa0 |
SendAgentDialog_Func |
(thunk) | 0x008105b0 |
Full catalog with sub-function breakdowns in docs/RE/reverse_engineering_reference.md.
The game uses a hash table (THashTable<IFrame::Msg::CHandler> at DAT_ram_005a0338) for message dispatch, not a switch statement. Messages fall into three ranges:
0x00–0x55— base frame lifecycle0x100000xx— server→client notifications (~90 mapped, ~15 unknown, ~6 newly discovered via WASM)0x300000xx— client→server commands (~30 mapped, all send-to-server actions)
The authoritative UIMessage enum is at C:\Users\Apo\Py4GW\vendor\gwca\Include\GWCA\Managers\UIMgr.h:294 (~120 entries). To discover missing messages, either hook SendUIMessage_Func at runtime (GWCA already does this) or run a Ghidra script against WASM callers of FrameMsgSendRegistered. Full procedure including the script is in docs/RE/reverse_engineering_reference.md Section 4.
| Layer | Path | Key Files |
|---|---|---|
| C++ (GWCA) | C:\Users\Apo\Py4GW\vendor\gwca\Source\ |
AgentMgr.cpp, UIMgr.cpp, GameThreadMgr.cpp |
| C++ (GWCA headers) | C:\Users\Apo\Py4GW\vendor\gwca\Include\GWCA\ |
Managers/AgentMgr.h, Utilities/Scanner.h |
| Python native | Py4GWCoreLib\native_src\ |
methods/PlayerMethods.py, internals/native_function.py |
| Python Scanner | Py4GWCoreLib\Scanner.py |
FindAssertion, FindInRange, ToFunctionStart |
| Ghidra EXE | /Gw.exe(Symbols) via MCP |
18,017 functions, x86:LE:32, base 0x00400000 |
| Ghidra WASM | /Gw.wasm via MCP |
18,004 functions, Wasm:LE:32, base ram:80000000 |
| GWCA Name | WASM Symbol | EXE Address |
|---|---|---|
DoWorldActon_Func |
CoreActionExecuteWorldAction |
0x0050e5e0 |
CallTarget_Func |
CharCliPlayerOrderAlertSimple |
0x00917740 |
ChangeTarget_Func |
IAgentView::SetSelections |
0x007e0f60 |
MoveTo_Func |
IUi::Game::Walk* |
0x00534fa0 |
SendAgentDialog_Func |
(thunk) | 0x008105b0 |
Full catalog with sub-function breakdowns in docs/RE/reverse_engineering_reference.md.
Py4GW_widget_manager.pyis the in-client widget bootstrap: it creates the manager INI key, runs widget discovery, and hands off toWidgets/WidgetCatalog/Py4GW_widget_catalog.py.Py4GW_Launcher.pyis the external launcher/injector UI.- Bridge stack wiring is split across:
- injected widget:
Widgets/Coding/Tools/Bridge Client.py - daemon:
bridge_daemon.py - operator CLI:
bridge_cli.py
- injected widget:
- MCP adapter entrypoint is
py4gw_mcp_server.py; it talks to the daemon over stdio->daemon bridging rather than directly to injected clients. - Bridge defaults are verified in code: widget server
127.0.0.1:47811, control server127.0.0.1:47812, and the CLI targets control port47812by default. Sources/modular_bot/contains the real ModularBot implementation. Files underWidgets/Automation/modularbot/are mostly thin wrappers that expose those tools/prebuilts through Widget Manager.
- Bridge help / argument discovery:
python "bridge_daemon.py" --helppython "bridge_cli.py" --help
- MCP adapter help / surface discovery:
python "py4gw_mcp_server.py" --help
- ModularBot docs coverage check:
python "Sources/modular_bot/tools/validate_modular_docs.py"
- For architecture questions, prefer module-specific imports and docs over the broad
Py4GWCoreLibfacade. The conceptual model treatsPy4GWCoreLibas the single Python-facing source-of-truth layer,py4gwcorelib_srcas support infrastructure, andGLOBAL_CACHEas a derivative consumer/cache layer. - The current MCP adapter intentionally exposes a narrow safe tool set over daemon control, not generic arbitrary bridge calls:
list_clients,list_namespaces,list_commands,describe_runtime,get_map_state,get_player_state, andlist_agents. - Widget discovery is folder-based, not file-based:
WidgetHandlerwalksWidgets/, and only folders containing a.widgetmarker are discovery roots; every.pyfile in that same folder is loaded as a widget. - Widget metadata defaults are non-obvious and come from
Py4GWCoreLib/py4gwcorelib_src/WidgetManager.py:MODULE_CATEGORYdefaults to the firstwidget_pathsegment,MODULE_TAGSdefaults to all path segments, andOPTIONALdefaults toFalseonly forSystemandPy4GWcategories. - Before touching follow-system code, read
FOLLOW_REFACTOR_HANDOVER.md. Py4GWCoreLib/GlobalCache/SharedMemory.pyis startup-sensitive and currently importsHeroAI.follow.leader_publishdirectly. Do not replace that with broad package-root imports.HeroAI/follow/__init__.pyintentionally exports nothing. Import exact submodules such asHeroAI.follow.leader_publish, notHeroAI.follow.- Avoid committing local runtime/config churn unless the task is specifically about them:
Py4GW.ini,Py4GW_Launcher.ini, andPy4GW_injection_log.txt. README documentsgit update-index --skip-worktreefor those files.