2V1-d: offline login -> select -> loading against a fake 40250 server
port.login_flow drives the real 40250 root scripts through the ported CPythonNetworkStream and CAccountConnector against a loopback auth + game server built from net/classic (wire layouts, sequence table, cipher) that answers like the 40250 desc.cpp/input*.cpp: handshake, key agreement, LOGIN3/AUTH_SUCCESS, LOGIN2, the character list, DirectEnter reconnect, CHARACTER_SELECT, the loading packets and CLIENT_VERSION, with every sequence byte checked. - NetStream::ActivateCipher decrypts ciphertext already buffered behind KEY_AGREEMENT_COMPLETED (PORT) - CPythonApplication is final (PORT): no application object exists on the platform yet, so its virtual overrides must be called directly - app.GetTime (GetGlobalTime) and camera/center-position stubs - SendClientVersionPacket verbatim in the PhaseGame stand-in Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
4d9b800efb
commit
8d9d863c7e
@@ -0,0 +1,59 @@
|
||||
#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;
|
||||
|
||||
~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; }
|
||||
|
||||
// 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);
|
||||
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_auth_port = 0, m_game_port = 0;
|
||||
std::thread m_thread;
|
||||
std::atomic<bool> m_stop{false};
|
||||
std::mutex m_mutex;
|
||||
std::vector<std::string> m_events;
|
||||
std::string m_error;
|
||||
};
|
||||
Reference in New Issue
Block a user