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>
This commit is contained in:
shenlei
2026-09-24 00:27:35 +09:00
co-authored by Claude Opus 5.5
parent be5a68ff35
commit ce9f96398e
13 changed files with 362 additions and 32 deletions
@@ -2,7 +2,6 @@
// The module constants of the 2V0 stub modules (GameplayModules.cpp), copied from each 40250
// module's init function with the preprocessor lines around them.
#include "UserInterface/StdAfx.h"
#include "UserInterface/ServerStateChecker.h"
#include "UserInterface/PythonSystem.h"
#include "UserInterface/PythonBackground.h"
#include "EterLib/StateManager.h"
@@ -36,12 +35,6 @@
#include "UserInterface/PythonPlayer.h"
#include "UserInterface/PythonTextTail.h"
// 40250 UserInterface/ServerStateCheckerModule.cpp
void AddStubConstants_ServerStateChecker(PyObject* poModule)
{
(void) poModule;
}
// 40250 UserInterface/PythonBackgroundModule.cpp
void AddStubConstants_background(PyObject* poModule)
{
@@ -5,7 +5,6 @@
#include "../EterLib/UIRenderCommands.h"
// GameplayModuleConstants.cpp: each module's 40250 constants.
void AddStubConstants_ServerStateChecker(PyObject* poModule);
void AddStubConstants_background(PyObject* poModule);
void AddStubConstants_event(PyObject* poModule);
void AddStubConstants_eventMgr(PyObject* poModule);
@@ -34,20 +33,6 @@ PyObject* stub_call_named(const char* name, PyObject* result = nullptr) {
return result ? result : Py_BuildValue("i", 0);
}
// 40250 UserInterface/ServerStateCheckerModule.cpp: ServerStateChecker export names.
PyObject* stub_ServerStateChecker_Create(PyObject*, PyObject*) { return stub_call_named("ServerStateChecker.Create", Py_BuildNone()); }
PyObject* stub_ServerStateChecker_Update(PyObject*, PyObject*) { return stub_call_named("ServerStateChecker.Update", Py_BuildNone()); }
PyObject* stub_ServerStateChecker_Request(PyObject*, PyObject*) { return stub_call_named("ServerStateChecker.Request", Py_BuildNone()); }
PyObject* stub_ServerStateChecker_AddChannel(PyObject*, PyObject*) { return stub_call_named("ServerStateChecker.AddChannel", Py_BuildNone()); }
PyObject* stub_ServerStateChecker_Initialize(PyObject*, PyObject*) { return stub_call_named("ServerStateChecker.Initialize", Py_BuildNone()); }
PyMethodDef methods_ServerStateChecker[] = {
{ "Create", stub_ServerStateChecker_Create, METH_VARARGS },
{ "Update", stub_ServerStateChecker_Update, METH_VARARGS },
{ "Request", stub_ServerStateChecker_Request, METH_VARARGS },
{ "AddChannel", stub_ServerStateChecker_AddChannel, METH_VARARGS },
{ "Initialize", stub_ServerStateChecker_Initialize, METH_VARARGS },
{ NULL, NULL }
};
// 40250 UserInterface/PythonBackgroundModule.cpp: background export names.
PyObject* stub_background_IsSoftwareTiling(PyObject*, PyObject*) { return stub_call_named("background.IsSoftwareTiling", Py_BuildValue("i", 0)); }
PyObject* stub_background_EnableSoftwareTiling(PyObject*, PyObject*) { return stub_call_named("background.EnableSoftwareTiling", Py_BuildNone()); }
@@ -892,11 +877,6 @@ PyMethodDef methods_udp[] = {
} // namespace
void init_2v0_gameplay_stubs() {
{
PyObject* module = Py_InitModule("ServerStateChecker", methods_ServerStateChecker);
PyModule_AddIntConstant(module, "__port_stub__", 1);
AddStubConstants_ServerStateChecker(module);
}
{
PyObject* module = Py_InitModule("background", methods_background);
PyModule_AddIntConstant(module, "__port_stub__", 1);
@@ -10,6 +10,7 @@
#include "../EterLib/HostCursor.h"
#include "UserInterface/PythonNetworkStream.h"
#include "UserInterface/AccountConnector.h"
#include "UserInterface/ServerStateChecker.h"
#include "UserInterface/PythonPlayer.h"
#include "GameLib/FlyingObjectManager.h"
#include "GameLib/RaceManager.h"
@@ -58,9 +59,11 @@ std::unique_ptr<CTimer> g_timer;
std::unique_ptr<CPythonNetworkStream> g_network_stream;
std::unique_ptr<CAccountConnector> g_account_connector;
// 40250 CPythonApplication::m_pyPlayer, m_FlyingManager and m_kEftMgr: the loading phase clears them
// (CPythonNetworkStream::SetLoadingPhase). PythonPlayer and FlyingObjectManager are platform/pending stand-ins.
// (CPythonNetworkStream::SetLoadingPhase).
std::unique_ptr<CPythonPlayer> g_player;
std::unique_ptr<CFlyingManager> g_flying_manager;
// 40250 CPythonApplication::m_kServerStateChecker: intrologin.py's channel status list.
std::unique_ptr<CServerStateChecker> g_server_state_checker;
std::unique_ptr<CRaceManager> g_race_manager;
std::unique_ptr<CGameEventManager> g_game_event_manager;
std::unique_ptr<CEffectManager> g_effect_manager;
@@ -314,6 +317,7 @@ bool Start(const char* stdlib_path, std::string* error)
g_account_connector = std::make_unique<CAccountConnector>();
g_player = std::make_unique<CPythonPlayer>();
g_flying_manager = std::make_unique<CFlyingManager>();
g_server_state_checker = std::make_unique<CServerStateChecker>();
g_race_manager = std::make_unique<CRaceManager>();
g_game_event_manager = std::make_unique<CGameEventManager>();
g_effect_manager = std::make_unique<CEffectManager>();
@@ -347,6 +351,7 @@ bool run_main_script_body(const char* lpCmdLine, std::string* error)
initnet();
initeffect();
initfly();
initServerStateChecker();
init_2v0_gameplay_stubs();
ClearLogBoxMessage();
@@ -517,6 +522,7 @@ void Stop()
g_effect_manager.reset();
g_game_event_manager.reset();
g_race_manager.reset();
g_server_state_checker.reset();
g_flying_manager.reset();
g_player.reset();
g_account_connector.reset();
@@ -0,0 +1,105 @@
#include "StdAfx.h"
#include "Packet.h"
#include "ServerStateChecker.h"
#include "../EterLib/NetAddress.h"
#pragma pack(push)
#pragma pack(1)
typedef unsigned char ServerStateChecker_Header;
typedef unsigned long ServerStateChecker_Key;
typedef unsigned long ServerStateChecker_Index;
typedef unsigned char ServerStateChecker_State;
#pragma pack(pop)
CServerStateChecker::CServerStateChecker()
{
Initialize();
}
CServerStateChecker::~CServerStateChecker()
{
Initialize();
m_poWnd = NULL;
}
void CServerStateChecker::Create(PyObject* poWnd)
{
m_poWnd = poWnd;
}
void CServerStateChecker::AddChannel(UINT uServerIndex, const char* c_szAddr, UINT uPort)
{
TChannel c;
c.uServerIndex = uServerIndex;
c.c_szAddr = c_szAddr;
c.uPort = uPort;
m_lstChannel.push_back(c);
}
void CServerStateChecker::Request()
{
if (m_lstChannel.empty()) {
return;
}
if (!m_kStream.Connect(m_lstChannel.begin()->c_szAddr, m_lstChannel.begin()->uPort))
{
for (std::list<TChannel>::const_iterator it = m_lstChannel.begin(); it != m_lstChannel.end(); ++it) {
PyCallClassMemberFunc(m_poWnd, "NotifyChannelState", Py_BuildValue("(ii)", it->uServerIndex, 0));
}
return;
}
m_kStream.ClearRecvBuffer();
m_kStream.SetSendBufferSize(1024);
m_kStream.SetRecvBufferSize(1024);
BYTE bHeader = HEADER_CG_STATE_CHECKER;
if (!m_kStream.Send(sizeof(bHeader), &bHeader))
{
for (std::list<TChannel>::const_iterator it = m_lstChannel.begin(); it != m_lstChannel.end(); ++it) {
PyCallClassMemberFunc(m_poWnd, "NotifyChannelState", Py_BuildValue("(ii)", it->uServerIndex, 0));
}
Initialize();
return;
}
}
void CServerStateChecker::Update()
{
m_kStream.Process();
BYTE bHeader;
if (!m_kStream.Recv(sizeof(bHeader), &bHeader)) {
return;
}
if (HEADER_GC_RESPOND_CHANNELSTATUS != bHeader) {
return;
}
int nSize;
if (!m_kStream.Recv(sizeof(nSize), &nSize)) {
return;
}
for (int i = 0; i < nSize; i++) {
TChannelStatus channelStatus;
if (!m_kStream.Recv(sizeof(channelStatus), &channelStatus)) {
return;
}
for (std::list<TChannel>::const_iterator it = m_lstChannel.begin(); it != m_lstChannel.end(); ++it) {
if (channelStatus.nPort == it->uPort) {
PyCallClassMemberFunc(m_poWnd, "NotifyChannelState", Py_BuildValue("(ii)", it->uServerIndex, channelStatus.bStatus));
break;
}
}
}
Initialize();
}
void CServerStateChecker::Initialize()
{
m_lstChannel.clear();
m_kStream.Disconnect();
}
@@ -0,0 +1,64 @@
#include "StdAfx.h"
#include "ServerStateChecker.h"
PyObject * ServerStateCheckerCreate(PyObject* poSelf, PyObject* poArgs)
{
PyObject * poWnd;
if (!PyTuple_GetObject(poArgs, 0, &poWnd))
return Py_BadArgument();
CServerStateChecker::Instance().Create(poWnd);
return Py_BuildNone();
}
PyObject * ServerStateCheckerUpdate(PyObject* poSelf, PyObject* poArgs)
{
CServerStateChecker::Instance().Update();
return Py_BuildNone();
}
PyObject * ServerStateCheckerRequest(PyObject* poSelf, PyObject* poArgs)
{
CServerStateChecker::Instance().Request();
return Py_BuildNone();
}
PyObject * ServerStateCheckerAddChannel(PyObject* poSelf, PyObject* poArgs)
{
int nServerIndex;
if (!PyTuple_GetInteger(poArgs, 0, &nServerIndex))
return Py_BuildException();
char * szAddr;
if (!PyTuple_GetString(poArgs, 1, &szAddr))
return Py_BuildException();
int nPort;
if (!PyTuple_GetInteger(poArgs, 2, &nPort))
return Py_BuildException();
CServerStateChecker::Instance().AddChannel(nServerIndex, szAddr, nPort);
return Py_BuildNone();
}
PyObject * ServerStateCheckerInitialize(PyObject* poSelf, PyObject* poArgs)
{
CServerStateChecker::Instance().Initialize();
return Py_BuildNone();
}
void initServerStateChecker()
{
static PyMethodDef s_methods[] =
{
{ "Create", ServerStateCheckerCreate, METH_VARARGS },
{ "Update", ServerStateCheckerUpdate, METH_VARARGS },
{ "Request", ServerStateCheckerRequest, METH_VARARGS },
{ "AddChannel", ServerStateCheckerAddChannel, METH_VARARGS },
{ "Initialize", ServerStateCheckerInitialize, METH_VARARGS },
{ NULL, NULL, NULL },
};
//PyObject * poModule = Py_InitModule("ServerStateChecker", s_methods);
Py_InitModule("ServerStateChecker", s_methods);
}
@@ -51,3 +51,4 @@ void initChat();
void initnet();
void initeffect();
void initfly();
void initServerStateChecker();