Files
mtgodot-poc/extension/tests/port_login_flow_server.h
T
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

71 lines
2.7 KiB
C++

#pragma once
// The server half of port_login_flow_test: a loopback auth server and game server that answer the way
// the 40250 game server (game/src/desc.cpp, input.cpp, input_login.cpp, input_db.cpp) does for the
// login → select → loading path. It is built from mtnet's classic wire layouts, sequence table and
// cipher (net/classic/), which were checked 1:1 against the 40250 server on 192.168.21.203, and shares
// no code with the ported client it talks to. Kept out of the client's translation unit: Packet.h's
// macros collide with wire_classic.h's constants.
#include <atomic>
#include <cstdint>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
class FakeLoginServer
{
public:
static constexpr const char* kLogin = "fakeuser";
static constexpr const char* kPassword = "fakepass";
static constexpr const char* kCharacterName = "FakeWarrior";
static constexpr std::uint32_t kLoginKey = 0x13572468;
static constexpr std::uint32_t kMainVID = 0x00012345;
static constexpr std::int32_t kX = 469300, kY = 964200;
// A ground item in view at entergame (CItem::EncodeInsertPacket): vnum 19, owned by the character.
static constexpr std::uint32_t kGroundItemVID = 0x00054321;
static constexpr std::uint32_t kGroundItemVnum = 19;
static constexpr std::int32_t kGroundItemX = kX + 150, kGroundItemY = kY;
~FakeLoginServer();
// Listens on two loopback ports and serves them from a thread until Stop().
bool Start();
void Stop();
int AuthPort() const { return m_auth_port; }
int GamePort() const { return m_game_port; }
// A third game-server port that only answers intrologin's ServerStateChecker (CG_STATE_CHECKER).
int StatePort() const { return m_state_port; }
// The channel port the state answer reports besides StatePort(), with status kOtherChannelStatus.
static constexpr int kOtherChannelPort = 13002;
static constexpr int kOtherChannelStatus = 1;
// Milestones in arrival order ("auth:login3", "game1:login2", ...), and the first protocol error.
std::vector<std::string> Events();
std::string Error();
bool Failed() { return !Error().empty(); }
bool Has(const std::string& event);
struct Connection;
private:
void Run();
bool ServeAuth(Connection& c);
bool ServeGame(Connection& c, int index);
bool Handshake(Connection& c, std::uint8_t phase_after);
void ServeState();
bool Accept(int listener, Connection& c);
void Event(const std::string& event);
bool Fail(const std::string& error);
int m_auth_listener = -1, m_game_listener = -1;
int m_state_listener = -1;
int m_auth_port = 0, m_game_port = 0, m_state_port = 0;
std::thread m_thread, m_state_thread;
std::atomic<bool> m_stop{false};
std::mutex m_mutex;
std::vector<std::string> m_events;
std::string m_error;
};