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>
This commit is contained in:
shenlei
2026-09-23 18:20:57 +09:00
co-authored by Claude Opus 5.5
parent d16fabf61c
commit 62b6e23bfc
76 changed files with 21087 additions and 753 deletions
@@ -12,12 +12,31 @@
#include "UserInterface/PythonPlayer.h"
#include "GameLib/FlyingObjectManager.h"
#include "EffectLib/EffectManager.h"
#include "GameLib/ItemManager.h"
#include "UserInterface/PythonCharacterManager.h"
#include "UserInterface/PythonItem.h"
#include "UserInterface/PythonShop.h"
#include "UserInterface/PythonExchange.h"
#include "UserInterface/PythonChat.h"
#include "UserInterface/PythonTextTail.h"
#include "UserInterface/PythonNonPlayer.h"
#include "UserInterface/PythonMiniMap.h"
#include "UserInterface/PythonEventManager.h"
#include "UserInterface/PythonBackground.h"
#include "UserInterface/PythonQuest.h"
#include "UserInterface/PythonMessenger.h"
#include "UserInterface/PythonSafeBox.h"
#include "UserInterface/PythonGuild.h"
#include "UserInterface/AbstractApplication.h"
#include "UserInterface/PythonApplication.h"
#include "EterBase/Timer.h"
#include "PythonBoot.h"
#include "PythonHost.h"
#include "../EterLib/UIRenderCommands.h"
#include "../EterBase/LogBox.h"
#include <condition_variable>
#include <cstdlib>
#include <memory>
#include <mutex>
#include <pthread.h>
@@ -39,6 +58,61 @@ std::unique_ptr<CAccountConnector> g_account_connector;
std::unique_ptr<CPythonPlayer> g_player;
std::unique_ptr<CFlyingManager> g_flying_manager;
std::unique_ptr<CEffectManager> g_effect_manager;
// PORT: CPythonApplication is not constructed by the Godot host yet. GamePhase calls the
// 40250 IAbstractApplication singleton, so expose the host's current timer and camera surface.
class GameApplicationAdapter final : public IAbstractApplication
{
public:
void GetMousePosition(POINT* point) override { if (point) *point = {}; }
float GetGlobalTime() override { return CTimer::Instance().GetCurrentSecond(); }
float GetGlobalElapsedTime() override { return CTimer::Instance().GetElapsedSecond(); }
void SkipRenderBuffering(DWORD) override {}
void SetServerTime(time_t) override {}
void SetCenterPosition(float x, float y, float z) override
{
CPythonApplication::Instance().SetCenterPosition(x, y, z);
}
void SetEventCamera(const SCameraSetting& setting) override
{
CPythonApplication::Instance().SetEventCamera(setting);
}
void BlendEventCamera(const SCameraSetting&, float) override {}
void SetDefaultCamera() override { CPythonApplication::Instance().SetDefaultCamera(); }
void RunIMEUpdate() override {}
void RunIMETabEvent() override {}
void RunIMEReturnEvent() override {}
void RunIMEChangeCodePage() override {}
void RunIMEOpenCandidateListEvent() override {}
void RunIMECloseCandidateListEvent() override {}
void RunIMEOpenReadingWndEvent() override {}
void RunIMECloseReadingWndEvent() override {}
};
std::unique_ptr<GameApplicationAdapter> g_game_application;
// The CPythonApplication members the game phase reaches through Instance() (批次 2V2), in 40250's member
// order. All are platform/pending stand-ins so far: their stub constructors leave scalar members unset, so
// the object is built in zeroed storage and inline header accessors read null/0 rather than garbage.
struct GameSingletons
{
static void* operator new(size_t size) { return std::calloc(1, size); }
static void operator delete(void* p) { std::free(p); }
CItemManager kItemMgr;
CPythonCharacterManager kChrMgr;
CPythonItem pyItem;
CPythonShop pyShop;
CPythonExchange pyExchange;
CPythonChat pyChat;
CPythonTextTail pyTextTail;
CPythonNonPlayer pyNonPlayer;
CPythonMiniMap pyMiniMap;
CPythonEventManager pyEventManager;
CPythonBackground pyBackground;
CPythonQuest pyQuest;
CPythonMessenger pyManager;
CPythonSafeBox pySafeBox;
CPythonGuild pyGuild;
};
std::unique_ptr<GameSingletons> g_game_singletons;
bool fail(std::string* error, const std::string& text)
{
@@ -225,6 +299,8 @@ bool Start(const char* stdlib_path, std::string* error)
g_player = std::make_unique<CPythonPlayer>();
g_flying_manager = std::make_unique<CFlyingManager>();
g_effect_manager = std::make_unique<CEffectManager>();
g_game_application = std::make_unique<GameApplicationAdapter>();
g_game_singletons = std::make_unique<GameSingletons>();
return true;
}
@@ -245,6 +321,7 @@ bool run_main_script_body(const char* lpCmdLine, std::string* error)
initgrpText();
initwndMgr();
initapp();
initChat();
initnet();
init_2v0_gameplay_stubs();
@@ -411,6 +488,8 @@ void Stop()
// 40250 Main(): pyLauncher.Clear() runs before app->Destroy()/delete app, so the window manager
// (a CPythonApplication member) is still alive while Py_Finalize runs the windows' __del__.
g_launcher->Clear();
g_game_singletons.reset();
g_game_application.reset();
g_effect_manager.reset();
g_flying_manager.reset();
g_player.reset();