- UserInterface/PythonShop.cpp ported verbatim (shop module, initshop after initfly); pending stub and module stubs removed. - 40250 PythonApplicationCursor.cpp and SetMouseHandler moved into the platform CPythonApplication on file-static members; GetCursorNum out of line, since uishop's app.GetCursor() dereferenced the missing application object. - Fake server: general store (npclist 9003) answers CG_ON_CLICK with GC_SHOP START, CG_SHOP BUY/SELL2/END like CShop::Buy / CShopManager::Sell. - port.login_flow step 10: click the shopkeeper, right-click buy, drag-sell, ESC close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
83 lines
3.6 KiB
C++
83 lines
3.6 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;
|
|
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;
|
|
|
|
~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;
|
|
};
|