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:
co-authored by
Claude Opus 5.5
parent
be5a68ff35
commit
ce9f96398e
@@ -142,9 +142,11 @@ bool FakeLoginServer::Start()
|
||||
{
|
||||
m_auth_listener = listen_loopback(&m_auth_port);
|
||||
m_game_listener = listen_loopback(&m_game_port);
|
||||
if (m_auth_listener < 0 || m_game_listener < 0)
|
||||
m_state_listener = listen_loopback(&m_state_port);
|
||||
if (m_auth_listener < 0 || m_game_listener < 0 || m_state_listener < 0)
|
||||
return false;
|
||||
m_thread = std::thread([this] { Run(); });
|
||||
m_state_thread = std::thread([this] { ServeState(); });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -153,7 +155,9 @@ void FakeLoginServer::Stop()
|
||||
m_stop = true;
|
||||
if (m_thread.joinable())
|
||||
m_thread.join();
|
||||
for (int* fd : {&m_auth_listener, &m_game_listener})
|
||||
if (m_state_thread.joinable())
|
||||
m_state_thread.join();
|
||||
for (int* fd : {&m_auth_listener, &m_game_listener, &m_state_listener})
|
||||
if (*fd >= 0)
|
||||
{
|
||||
close(*fd);
|
||||
@@ -287,6 +291,52 @@ bool FakeLoginServer::Handshake(Connection& c, std::uint8_t phase_after)
|
||||
return c.Phase(phase_after) || Fail("send phase after handshake");
|
||||
}
|
||||
|
||||
// CServerStateChecker::Request connects to the first channel and sends the bare header. The game server
|
||||
// has already started the handshake (DESC::Setup), so the client reads past those bytes one per Update;
|
||||
// CInputHandshake::Analyze(HEADER_CG_STATE_CHECKER) asks the db, and CInputDB::RespondChannelStatus writes
|
||||
// the header, the count, the TChannelStatus {short nPort; BYTE bStatus} array and a success byte.
|
||||
void FakeLoginServer::ServeState()
|
||||
{
|
||||
Connection c;
|
||||
c.stop = &m_stop;
|
||||
for (;;)
|
||||
{
|
||||
if (m_stop)
|
||||
return;
|
||||
pollfd pfd = {m_state_listener, POLLIN, 0};
|
||||
if (poll(&pfd, 1, 50) > 0)
|
||||
break;
|
||||
}
|
||||
c.fd = accept(m_state_listener, nullptr, nullptr);
|
||||
if (c.fd < 0)
|
||||
return;
|
||||
mtnet::classic::Handshake hs = {HDR_HANDSHAKE, kHandshake, now_ms(), 0};
|
||||
if (!c.Phase(PHASE_HANDSHAKE) || !c.Send(hs))
|
||||
return;
|
||||
std::uint8_t header = 0;
|
||||
if (c.Read(&header, 1) != 1 || header != 206)
|
||||
{
|
||||
Fail("state checker: expected CG_STATE_CHECKER");
|
||||
return;
|
||||
}
|
||||
Event("state:checker");
|
||||
std::vector<std::uint8_t> out = {210};
|
||||
const std::int32_t count = 2;
|
||||
out.insert(out.end(), reinterpret_cast<const std::uint8_t*>(&count), reinterpret_cast<const std::uint8_t*>(&count) + 4);
|
||||
for (int port : {m_state_port, kOtherChannelPort})
|
||||
{
|
||||
const std::int16_t short_port = static_cast<std::int16_t>(port);
|
||||
out.insert(out.end(), reinterpret_cast<const std::uint8_t*>(&short_port), reinterpret_cast<const std::uint8_t*>(&short_port) + 2);
|
||||
out.push_back(static_cast<std::uint8_t>(kOtherChannelStatus));
|
||||
}
|
||||
out.push_back(1);
|
||||
c.Send(out.data(), out.size());
|
||||
// Keep the socket open until the client disconnects (CServerStateChecker::Initialize).
|
||||
std::uint8_t rest;
|
||||
while (c.Read(&rest, 1) == 1)
|
||||
;
|
||||
}
|
||||
|
||||
// Reads one client packet of a known header: the static size from packet_info.cpp plus the sequence
|
||||
// byte CInputProcessor::Process checks against gc_abSequence.
|
||||
static int read_packet(FakeLoginServer::Connection& c, std::uint8_t expect, void* packet, size_t size,
|
||||
|
||||
Reference in New Issue
Block a user