#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 #include #include #include #include #include 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; static constexpr std::uint32_t kExchangeItemVnum = 19, kExchangeGold = 1000; // A stray dog (npclist 101) for the combat step; three hits kill it. The spawn point lies in the village's // ATTRIBUTE_BANPK disc (CInstanceBase::IsInSafe blocks every hit there), so the dog stands 18 m north of it. static constexpr std::uint32_t kMobVID = 0x00023456, kMobRace = 101; static constexpr std::int32_t kMobX = kX, kMobY = kY - 1800; static constexpr int kMobHits = 3; // A general store (npclist 9003 "goods") 4 m east of the dog, for the shop step. Its one shelf item is a // small red potion; selling it back pays kShopSellPrice (CShopManager::Sell: proto price / 5 minus 3 % tax). static constexpr std::uint32_t kShopVID = 0x00034567, kShopRace = 9003; static constexpr std::int32_t kShopX = kMobX + 400, kShopY = kMobY; static constexpr std::uint32_t kShopItemVnum = 27001, kShopItemPrice = 50; static constexpr std::int32_t kStartGold = 10000, kShopSellPrice = 9; // An old man (npclist 20009) 0.5 m east of the store whose click runs a quest dialogue the way CQuestManager // sends it: say + select ([QUESTION]), then a [NEXT] page, then a [DONE] page. static constexpr std::uint32_t kQuestVID = 0x00045678, kQuestRace = 20009; static constexpr std::int32_t kQuestX = kShopX + 50, kQuestY = kShopY; // A storekeeper (npclist 9005) 1.5 m west of the shop whose click runs ShowMeSafeboxPassword, // receives /safebox_password, opens safebox, handles checkin/checkout and close. static constexpr std::uint32_t kSafeboxVID = 0x00056789, kSafeboxRace = 9005; static constexpr std::int32_t kSafeboxX = kShopX - 150, kSafeboxY = kShopY; static constexpr std::uint8_t kSafeboxSize = 9; static constexpr std::uint32_t kSafeboxItemVnum = 10; static constexpr std::int32_t kSafeboxMoney = 500; // Messenger & Guild constants (批次 4-f) static constexpr const char* kFriendName = "TestFriend"; static constexpr std::uint32_t kGuildID = 1; static constexpr const char* kGuildName = "TestGuild"; ~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 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 m_stop{false}; std::mutex m_mutex; std::vector m_events; std::string m_error; };