Commit Graph
26 Commits
Author SHA1 Message Date
shenleiandClaude Opus 5.5 22ca5da387 UserInterface: 32-bit LONG sockets in TItemData/TShopItemData
The 40250 wire structs hold Win32 long (4 bytes) sockets; on LP64 `long`
is 8, so each TShopItemData was too wide and SHOP_SUBHEADER_GC_START/_EX
misread every item after the first. Pin the wire size with a static_assert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:56:36 +09:00
shenandshen 25dd65f2ce feat: complete native client rendering parity updates 2026-09-27 19:44:41 -07:00
shenandshen 8a759f5718 fix(port): preserve 64-bit quest image pointer in quest.GetQuestData 2026-09-24 01:20:04 -07:00
shenandshen a57c83dd71 Build self-contained macOS client and verify native beta path 2026-09-24 00:59:08 -07:00
shenandshen 837c830182 port: complete 100% logic layer, eliminate pending dir, and unify client_main on port route
- Completed batch 4-f (Messenger, Guild, GuildMark), 4-g (PythonSystem, systemSetting), 4-h (PythonSoundManagerModule, MilesLib CSoundManager)
- Ported UserInterface/Locale.cpp, EterLocale/Japanese.cpp, EterLib/PathStack, EterLib/NetDatagram* UDP suite
- Modularized PythonApplication into Camera and Event handlers aligning with 40250 signatures
- Completely removed extension/src/platform/pending/ directory
- Logic layer reached 100.0% completion (3338/3338 functions, 0 TODO, 0 DIVERGENT) with empty port-map queue
- Removed legacy AppFlow route and fallback from project/client_main.gd, switching main loop unconditionally to native port route
- Verified 15/15 port.login_flow integration steps and 19/19 port_gate suites passing
2026-09-23 22:17:59 -07:00
shenlei ee7a9cd3e2 WIP port 4-d quest dialogue modules 2026-09-24 09:53:02 +09:00
shenleiandClaude Opus 5.5 a12e7fea42 Port route 4-c: NPC shop (PythonShop.cpp) + app cursor state
- UserInterface/PythonShop.cpp ported verbatim (shop module, initshop after initfly);
  pending stub and module stubs removed.
- 40250 PythonApplicationCursor.cpp and SetMouseHandler moved into the platform
  CPythonApplication on file-static members; GetCursorNum out of line, since uishop's
  app.GetCursor() dereferenced the missing application object.
- Fake server: general store (npclist 9003) answers CG_ON_CLICK with GC_SHOP START,
  CG_SHOP BUY/SELL2/END like CShop::Buy / CShopManager::Sell.
- port.login_flow step 10: click the shopkeeper, right-click buy, drag-sell, ESC close.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 09:30:47 +09:00
shenleiandClaude Opus 5.5 6ea0fa9889 port 4-a: text input through CPythonIME (login typed, in-game chat)
- UserInterface/PythonIME(+Module) ported verbatim; ime module stubs removed, initime() after initdbg()
- EterLib/IME.cpp in platform/: 40250 buffer/WMChar bodies, IMM/TSF processors no-op; committed
  multi-byte text via PythonBoot::UIChar emulating GCS_RESULTSTR
- GameApplicationAdapter RunIME* -> CWindowManager; Godot keys -> ui_ime_key/ui_char
- port.login_flow/login_live type the account through the IME; chat CG_CHAT round-trip on the fake server
- python_game_render_test logs in with Godot key events

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 08:35:53 +09:00
shenleiandClaude Opus 5.5 2ead95756e port: CMapOutdoor + PythonBackground + minimap (1:1 40250)
GameLib map/area/terrain units, PRTerrainLib Terrain/TextureSet,
PythonBackground(+Module) and ColorTransitionHelper ported verbatim;
pending stand-ins and background module stubs removed. Hand edits:
BeginEnvironment's pointer tag via uintptr_t, LoadWaterMap reads the
file's 32-bit longs. Terrain draw passes stay platform no-ops (Godot
draws the terrain). RecordingDevice gains textures, buffers and render
targets; orthographic draws become UI commands so CPythonMiniMap's
masked tiles show through minimap_image_filter in python_ui_surface.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 03:50:09 +09:00
shenleiandClaude Opus 5.5 03de3f314a port: CPythonSkill + skill module + EterBase/Poly (1:1 40250)
Copy PythonSkill.cpp and the EterBase/Poly formula parser verbatim, drop the
pending stand-in and the skill module stub, register initskill() after
initfly() and build CPythonSkill in m_pySkill's place. LoadLocaleData now
loads skilldesc/skilltable. Skill and item icon handles go through
Py_BuildPointer/PyTuple_GetPointer (ILP32 ints truncate 64-bit pointers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 03:07:15 +09:00
shenleiandClaude Opus 5.5 7358bf444a port: P3-e PythonMiniMap + miniMap module (1:1 40250)
Copy PythonMiniMap.cpp/PythonMiniMapModule.cpp and PRTerrainLib/StdAfx.cpp
verbatim, drop the pending stand-in and the miniMap module stub, register
initMiniMap() after initnet(). PR_FLOAT_TO_INTASM uses lrintf off MSVC;
D3DXColorModulate added. CMapOutdoor is not ported, so the pending
CMapManager::IsMapOutdoor() returns false (DIVERGENT) and the minimap's terrain
and atlas paths stay on 40250's no-outdoor-map branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 01:18:57 +09:00
shenleiandClaude Opus 5.5 070a27b2cb port: P3-d PythonTextTail + textTail module (1:1 40250)
Copy PythonTextTail.cpp/PythonTextTailModule.cpp verbatim, drop the pending
stand-in and the textTail module stub, register initTextTail() after
initChat() and call CPythonTextTail::Initialize() in Create as 40250 does.
Implement CScreen::ProjectPosition/UnprojectPosition (were stubs) with
D3DXVec3Project/Unproject, and ApplicationStringTable_GetString(DWORD) over
UserInterface.rc's English string table. port.login_flow projects the ground
item's name tail in an ALT frame and picks it back by screen position.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 01:10:41 +09:00
shenleiandClaude Opus 5.5 72b19b677d port: P3-c PythonExchange + exchange module (1:1 40250)
Copy PythonExchange.cpp/PythonExchangeModule.cpp verbatim, drop the pending
stand-in and the exchange module stub, register initTrade() after
initNonPlayer(). The fake server answers CG_EXCHANGE START with the partner's
item + gold and CANCEL with END; port.login_flow drives the exchange round-trip
through the Python exchange/net modules and uiexchange.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 00:59:05 +09:00
shenleiandClaude Opus 5.5 9298500df9 port: PythonItem + item module (batch 2 P3-b)
Verbatim 40250 CPythonItem and the item Python module replace the
pending stand-in and the module stub; CPythonApplication::Create calls
m_pyItem.Create() in 40250 order. itemGetIconImage returns its handle
through Py_BuildPointer (the ILP32 int truncated it and wndMgr.SetSlot
crashed on the live inventory). CMapManager::GetNormal evaluates
CTerrain::CalculateNormal on the native height tile; the item text-tail
members stay stubs, Pick answering -1 like an empty tail map.

The fake server drops vnum 19 next to the actor at entergame and answers
CG_ITEM_PICKUP with GC_ITEM_GROUND_DEL; login_flow picks it up through
player.PickCloseItem and checks item.SelectItem(19).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 00:50:16 +09:00
shenleiandClaude Opus 5.5 f0564a7f12 port: ItemData, ItemManager, PythonNonPlayer + LoadLocaleData (batch 2 P3-a)
Verbatim 40250 GameLib ItemData/ItemManager and UserInterface
PythonNonPlayer(+Module) replace their pending stand-ins and the
nonplayer module stub. LoadLocaleData's body moves into the platform
CPythonApplication stand-in, so the select phase loads item_proto and
mob_proto; CPythonSkill is still pending, so it returns false after the
tables load. login_flow checks item 19 (Sword+9) and mob 101 (Wild Dog),
offline and live.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 00:36:10 +09:00
shenleiandClaude Opus 5.5 ce9f96398e port: ServerStateChecker + module (batch 2 P2)
Verbatim 40250 CServerStateChecker and the ServerStateChecker Python
module replace the stub; PythonBoot owns the instance like
CPythonApplication::m_kServerStateChecker. The fake server gets a state
port that answers CG_STATE_CHECKER the way CInputHandshake and
CInputDB::RespondChannelStatus do; login_flow requests the channel list
after the character list (live: {1: 1, 2: 1}).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 00:27:35 +09:00
shenleiandClaude Opus 5.5 9dcf34dffc port: FlyingData/Instance/ObjectManager/FlyTrace + effect/fly modules (batch 2 P1)
Verbatim copies of the 40250 flying-object units and the effect/fly
Python modules replace the pending CFlyingManager stand-in and the two
module stubs; initeffect/initfly run after initnet as in 40250 and the
flying manager gets the background as its map manager.
CMapManager::GetTerrainHeight is added over the native height tiles.
login_flow checks playersettingmodule registered the indexed fly data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 00:12:19 +09:00
shenleiandClaude Opus 5.5 ff0983d3c8 2V3-a: walk with the keyboard through the ported player chain
PythonPlayer, PythonPlayerInput{,Keyboard,Mouse}, PythonPlayerEventHandler,
PythonPlayerSkill, PythonPlayerModule and GameType are verbatim copies; the
`player` stub module and the pending PythonPlayer/GameType stand-ins are gone.
Unported callees (CPythonSkill, CItemData, CItemManager, CPythonItem, ...) are
pending stubs. 64-bit fixes: emotion icon handles use Py_BuildPointer, and
PyDict_Next takes Py_ssize_t.

The actor did not move because GrannyUpdateModelMatrix passed the model matrix
through. Walk/run motions use constant root-motion extraction
(AccumulationFlags=AccumulationExtracted, LoopTranslation e.g. (0,-300,0) for
the warrior run, same as the .msa Accumulation). libgr2 now reads both fields
from TrackGroups[0], and the runtime moves by LoopTranslation over each
control's local-clock span, blended by control weight. VDA extraction is not
implemented.

port.login_flow presses DIK_UP; the actor walks more than 100 cm, and the fake
server parses CG packets and sees CG_MOVE FUNC_MOVE, then FUNC_WAIT at the new
position. port.login_live against the real server walked 132 cm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 23:41:31 +09:00
shenleiandClaude Opus 5.5 5266b00c6e port 2V2-d.1: ActorInstance/InstanceBase closure creates the main character
Port the ActorInstance*/InstanceBase* siblings plus GameUtil, FlyTarget,
PhysicsObject, GameEventManager, WeaponTrace, MapUtil and lineintersect_utils
verbatim and drop their pending stand-ins; CGameEventManager and CPythonSystem
join PythonBoot in CPythonApplication member order.

Root cause of the intermittent Loading-phase header 7: TPlayerSkill::tNextRead
is time_t, 32-bit in 40250 (_USE_32BIT_TIME_T) but 64-bit on LP64, so
SKILL_LEVEL (76) was read as 2551 bytes instead of 1531. Packet.h now uses
LONG with a static_assert, and the fake server sends SKILL_LEVEL in Loading.

The fake server's CHARACTER_ADD now uses CHAR_TYPE_PC; port.login_flow
asserts the main instance exists with race 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:02:21 +09:00
shenleiandClaude Opus 5.5 996d793f0c port 2V2-c: chrmgr/chr modules + race registration chain
PythonCharacterManagerModule/PythonCharacterModule ported verbatim; the
2V0 chr/chrmgr stubs are gone and initchr/initchrmgr run in 40250 order.
The registration chain they need (RaceData/RaceDataFile/RaceManager/
RaceMotionData, GameType, AttributeData/AttributeInstance, TextFileLoader,
Util, Camera/CameraProcedure) is ported; CRaceManager is created in
PythonBoot. port.login_flow asserts race 0 has its wait motions and no
unhandled packet header; port.login_live 8/8 against 192.168.21.203.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 18:40:47 +09:00
shenleiandClaude Opus 5.5 62b6e23bfc port 2V2-b: GamePhase dispatch consumes the Entergame burst
PythonNetworkStreamPhaseGame/PhaseGameActor/PhaseGameItem/Command,
NetworkActorManager and PythonChat/PythonChatModule are ported verbatim and
dispatched at runtime; the 2V0 chat stub module is removed and initChat()
runs before initnet() as in 40250. InstanceBase, PythonCharacterManager and
GameLib ActorInstance/RaceData/RaceManager compile but stay TODO until
2V2-c/d wire them.

Units not ported yet get their singletons from PythonBoot's GameSingletons
(CPythonApplication member order) and platform/pending stand-ins generated
by platform_stub.py pending; const char* stand-ins return "".

port.login_flow's fake server now sends the CInputLogin::Entergame order
(actor insert, NPC positions, PHASE_GAME, land list, time, channel, greet
notice, ping) and a TraceError observer asserts no unknown packet header.
port.login_live passes against the real server for 10 s of game phase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 18:20:57 +09:00
shenleiandClaude Opus 5.5 d16fabf61c 2V2-a: game.GameWindow opens after Loading and sends CG_ENTERGAME
The 2V0 stub modules now carry the 40250 module constants (copied from each
module's init function by stub_constants.py) and return zero values shaped like
each reference function's Py_BuildValue, so interfaceModule.MakeInterface runs
through (uiSafebox no longer divides by a zero SAFEBOX_SLOT_Y_COUNT).

UserInterface/StdAfx.h includes Locale.h before GameType.h as the 40250 PCH
does; the reversed order dropped GameType.h's ENABLE_NEW_EQUIPMENT_SYSTEM
branches.

port.login_flow's fake server checks CG_ENTERGAME; port.login_live reaches the
GameWindow on 192.168.21.203.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 16:56:23 +09:00
shenleiandClaude Opus 5.5 8d9d863c7e 2V1-d: offline login -> select -> loading against a fake 40250 server
port.login_flow drives the real 40250 root scripts through the ported
CPythonNetworkStream and CAccountConnector against a loopback auth + game
server built from net/classic (wire layouts, sequence table, cipher) that
answers like the 40250 desc.cpp/input*.cpp: handshake, key agreement,
LOGIN3/AUTH_SUCCESS, LOGIN2, the character list, DirectEnter reconnect,
CHARACTER_SELECT, the loading packets and CLIENT_VERSION, with every
sequence byte checked.

- NetStream::ActivateCipher decrypts ciphertext already buffered behind
  KEY_AGREEMENT_COMPLETED (PORT)
- CPythonApplication is final (PORT): no application object exists on the
  platform yet, so its virtual overrides must be called directly
- app.GetTime (GetGlobalTime) and camera/center-position stubs
- SendClientVersionPacket verbatim in the PhaseGame stand-in

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 15:20:32 +09:00
shenleiandClaude Opus 5.5 4d9b800efb 2V1-b/c: PythonNetworkStream phases, AccountConnector and the net module
Port PythonNetworkStream with its HandShake/Login/Select/Loading/Offline
phases, AccountConnector and PythonNetworkStreamModule verbatim; the net
stub goes. PythonBoot creates the network stream, account connector,
player, flying manager and effect manager like CPythonApplication, and
app.Create/Process drive CNetworkDevice and both connectors.

Packet.h long fields become 32-bit LONG; port.packet checks 38
login/select/loading packet sizes against wire_classic.h. Unported units
in the link closure live under platform/pending/ as stand-ins. The macOS
gate now builds all and finds iconv.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 15:02:14 +09:00
shenandshen b221a9eaf5 Port 2V0 UI Python bindings and app module link surface 2026-09-22 19:57:35 -07:00
shenleiandClaude Opus 5 2b873f190f port(2P step 3a/3b): stdlib on sys.path + the pack module
step 3a — 标准库:
  tools/py_embed/make_stdlib_zip.py packs the vendored Lib/ into
  <build>/python27.zip (397 modules, 6876547 bytes, ZIP_STORED — the zlib
  module is trimmed out of our CPython, so zipimport cannot inflate).
  The mtpython_stdlib target builds it; py_embed_test and the launcher test
  both run against it.
  platform/ScriptLib/PythonHost sets the embedding flags before
  Py_Initialize and, after it, puts the zip on sys.path and re-registers
  encodings.search_function with codecs — 2.7 builds the codec registry
  once, at startup, so replacing sys.path afterwards needs it back.

step 3b — pack 模块:
  UserInterface/PythonPackModule.cpp copied verbatim; packExist/packGet/
  initpack read root/ and uiscript/ through CEterPackManager, i.e. through
  asset_io. The CHINA_CRYPT_KEY block in that unit is CAccountConnector's
  and is kept #if 0 until the login slice (2V1). UserInterface/StdAfx.h is
  a documented partial mirror — the original is the whole client's PCH.

The launcher test now runs initpack() as RunMainScript does (its first
line), reads system.py / uiscript / locale text back out of the packs, and
confirms cp1252 decoding works off the zip. system.py still stops at
"No module named app" — the C++ modules are the 2V0 slice.

gates: port_map.py check 0 errors · key leak check 8/8 none · macOS ctest
27/27 · android + ios port_platform compile clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 09:16:37 +09:00