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>
This commit is contained in:
shenlei
2026-09-23 18:40:47 +09:00
co-authored by Claude Opus 5.5
parent 62b6e23bfc
commit 996d793f0c
39 changed files with 8270 additions and 625 deletions
+25
View File
@@ -40,6 +40,10 @@
#include "../src/platform/ScriptLib/PythonHost.h"
#include "../src/platform/UserInterface/UserInterface.h"
#include "../src/platform/EterBase/TraceErrorObserver.h"
#include "GameLib/StdAfx.h"
#include "GameLib/RaceManager.h"
#include "GameLib/RaceData.h"
#include "GameLib/RaceMotionData.h"
#include "port_login_flow_server.h"
#include <memory>
@@ -68,10 +72,21 @@ static int g_failures = 0;
static std::mutex g_syserr_mutex;
static std::vector<std::string> g_syserr;
// The two headers CheckPacket saw last (UserInterface/PythonNetworkStream.cpp).
extern int g_iLastPacket[2];
// CPythonNetworkStream::RecvDefaultPacket's CP949 "unhandled packet header %d, state %s": a phase
// dispatcher dropped the whole recv buffer.
static const char kUnhandledPacket[] = "\xc3\xb3\xb8\xae\xb5\xc7\xc1\xf6 \xbe\xca\xc0\xba \xc6\xd0\xc5\xb6 \xc7\xec\xb4\xf5";
static void on_trace_error(const char* line)
{
std::lock_guard<std::mutex> lock(g_syserr_mutex);
g_syserr.emplace_back(line);
// RecvDefaultPacket does not print the preceding headers; the packet before a stray header is
// the one whose size disagrees with the server.
if (std::string(line).find(kUnhandledPacket) != std::string::npos)
std::fprintf(stderr, "port_login_flow_test: unhandled packet after headers %d, %d\n", g_iLastPacket[0], g_iLastPacket[1]);
}
static int count_syserr(const char* needle)
@@ -277,9 +292,19 @@ int main(int argc, char** argv)
CHECK(py_true(gaming));
CHECK(py("import net\nassert net.IsConnect(), 'disconnected in the game phase'"));
CHECK(count_syserr("Unknown packet header") == 0);
CHECK(count_syserr(kUnhandledPacket) == 0);
CHECK(count_syserr("does not handle this header") == 0);
// The fake greet notice went through RecvChatPacket into the ported CPythonChat, where
// uichat's chat set (created by the game interface) counts it.
CHECK(live || py("import chat\nassert any(chat.GetLineCount(i) > 0 for i in range(4)), 'greet notice not in chat'"));
// playerSettingModule ran its race registration through the real chrmgr module: the warrior
// (race 0) has a general-mode wait motion in its CRaceData.
CHECK(py("import chr, chrmgr\nassert not hasattr(chr, '__port_stub__') and not hasattr(chrmgr, '__port_stub__')"));
CRaceData* race = nullptr;
const CRaceData::TMotionVector* waits = nullptr;
CHECK(CRaceManager::Instance().GetRaceDataPointer(0, &race) && race &&
race->GetMotionVectorPointer(CRaceMotionData::MODE_GENERAL, CRaceMotionData::NAME_WAIT, &waits) &&
waits && !waits->empty());
}
if (server)
{