tests: exercise mall open in smoke login
This commit is contained in:
@@ -27,6 +27,8 @@ constexpr size_t ACCOUNT_STATUS_MAX_LEN_LOCAL = 8;
|
|||||||
constexpr size_t PLAYER_PER_ACCOUNT_LOCAL = 4;
|
constexpr size_t PLAYER_PER_ACCOUNT_LOCAL = 4;
|
||||||
constexpr size_t CHARACTER_NAME_MAX_LEN_LOCAL = 64;
|
constexpr size_t CHARACTER_NAME_MAX_LEN_LOCAL = 64;
|
||||||
constexpr size_t GUILD_NAME_MAX_LEN_LOCAL = 12;
|
constexpr size_t GUILD_NAME_MAX_LEN_LOCAL = 12;
|
||||||
|
constexpr size_t SAFEBOX_PASSWORD_MAX_LEN_LOCAL = 6;
|
||||||
|
constexpr uint8_t CHAT_TYPE_TALKING_LOCAL = 0;
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct PacketGCPhase
|
struct PacketGCPhase
|
||||||
@@ -77,6 +79,13 @@ struct PacketCGLogin2
|
|||||||
uint32_t login_key;
|
uint32_t login_key;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PacketCGChat
|
||||||
|
{
|
||||||
|
uint16_t header;
|
||||||
|
uint16_t length;
|
||||||
|
uint8_t type;
|
||||||
|
};
|
||||||
|
|
||||||
struct SimplePlayerLocal
|
struct SimplePlayerLocal
|
||||||
{
|
{
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
@@ -216,6 +225,19 @@ struct PacketGCChannel
|
|||||||
uint16_t length;
|
uint16_t length;
|
||||||
uint8_t channel;
|
uint8_t channel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PacketGCSafeboxSize
|
||||||
|
{
|
||||||
|
uint16_t header;
|
||||||
|
uint16_t length;
|
||||||
|
uint8_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PacketGCSafeboxWrongPassword
|
||||||
|
{
|
||||||
|
uint16_t header;
|
||||||
|
uint16_t length;
|
||||||
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
struct SmokeOptions
|
struct SmokeOptions
|
||||||
@@ -225,6 +247,7 @@ struct SmokeOptions
|
|||||||
std::string client_version = "1215955205";
|
std::string client_version = "1215955205";
|
||||||
std::string expect_auth_failure;
|
std::string expect_auth_failure;
|
||||||
std::string expect_channel_failure;
|
std::string expect_channel_failure;
|
||||||
|
std::string mall_password;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SmokeResult
|
struct SmokeResult
|
||||||
@@ -240,6 +263,8 @@ struct SmokeResult
|
|||||||
std::string character_name;
|
std::string character_name;
|
||||||
bool character_created = false;
|
bool character_created = false;
|
||||||
int game_channel = -1;
|
int game_channel = -1;
|
||||||
|
bool mall_opened = false;
|
||||||
|
int mall_size = -1;
|
||||||
int64_t auth_handshake_ms = -1;
|
int64_t auth_handshake_ms = -1;
|
||||||
int64_t auth_login_ms = -1;
|
int64_t auth_login_ms = -1;
|
||||||
int64_t channel_handshake_ms = -1;
|
int64_t channel_handshake_ms = -1;
|
||||||
@@ -247,6 +272,7 @@ struct SmokeResult
|
|||||||
int64_t character_create_ms = -1;
|
int64_t character_create_ms = -1;
|
||||||
int64_t character_select_ms = -1;
|
int64_t character_select_ms = -1;
|
||||||
int64_t entergame_ms = -1;
|
int64_t entergame_ms = -1;
|
||||||
|
int64_t mall_open_ms = -1;
|
||||||
std::vector<std::string> events;
|
std::vector<std::string> events;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -333,6 +359,8 @@ void PrintJson(const SmokeResult& result)
|
|||||||
<< ",\"character_name\":\"" << JsonEscape(result.character_name) << "\""
|
<< ",\"character_name\":\"" << JsonEscape(result.character_name) << "\""
|
||||||
<< ",\"character_created\":" << (result.character_created ? "true" : "false")
|
<< ",\"character_created\":" << (result.character_created ? "true" : "false")
|
||||||
<< ",\"game_channel\":" << result.game_channel
|
<< ",\"game_channel\":" << result.game_channel
|
||||||
|
<< ",\"mall_opened\":" << (result.mall_opened ? "true" : "false")
|
||||||
|
<< ",\"mall_size\":" << result.mall_size
|
||||||
<< ",\"timings_ms\":{"
|
<< ",\"timings_ms\":{"
|
||||||
<< "\"auth_handshake\":" << result.auth_handshake_ms
|
<< "\"auth_handshake\":" << result.auth_handshake_ms
|
||||||
<< ",\"auth_login\":" << result.auth_login_ms
|
<< ",\"auth_login\":" << result.auth_login_ms
|
||||||
@@ -341,6 +369,7 @@ void PrintJson(const SmokeResult& result)
|
|||||||
<< ",\"character_create\":" << result.character_create_ms
|
<< ",\"character_create\":" << result.character_create_ms
|
||||||
<< ",\"character_select\":" << result.character_select_ms
|
<< ",\"character_select\":" << result.character_select_ms
|
||||||
<< ",\"entergame\":" << result.entergame_ms
|
<< ",\"entergame\":" << result.entergame_ms
|
||||||
|
<< ",\"mall_open\":" << result.mall_open_ms
|
||||||
<< "},\"events\":[";
|
<< "},\"events\":[";
|
||||||
|
|
||||||
for (size_t i = 0; i < result.events.size(); ++i)
|
for (size_t i = 0; i < result.events.size(); ++i)
|
||||||
@@ -470,6 +499,14 @@ public:
|
|||||||
WriteExact(m_fd, &encrypted, sizeof(encrypted), "write encrypted packet");
|
WriteExact(m_fd, &encrypted, sizeof(encrypted), "write encrypted packet");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendEncryptedFrame(const void* data, size_t length)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> encrypted(length);
|
||||||
|
std::memcpy(encrypted.data(), data, length);
|
||||||
|
m_cipher.EncryptInPlace(encrypted.data(), encrypted.size());
|
||||||
|
WriteExact(m_fd, encrypted.data(), encrypted.size(), "write encrypted frame");
|
||||||
|
}
|
||||||
|
|
||||||
bool WaitForFrame(std::vector<uint8_t>& frame, int timeout_ms)
|
bool WaitForFrame(std::vector<uint8_t>& frame, int timeout_ms)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
@@ -763,6 +800,17 @@ void SendClientVersion(EncryptedClient& channel_client, std::string_view client_
|
|||||||
EmitEvent(result, json, "client_version " + std::string(version_packet.timestamp));
|
EmitEvent(result, json, "client_version " + std::string(version_packet.timestamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendChatCommand(EncryptedClient& channel_client, std::string_view command)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> frame(sizeof(PacketCGChat) + command.size() + 1, 0);
|
||||||
|
auto* chat = reinterpret_cast<PacketCGChat*>(frame.data());
|
||||||
|
chat->header = CG::CHAT;
|
||||||
|
chat->length = static_cast<uint16_t>(frame.size());
|
||||||
|
chat->type = CHAT_TYPE_TALKING_LOCAL;
|
||||||
|
std::memcpy(frame.data() + sizeof(PacketCGChat), command.data(), command.size());
|
||||||
|
channel_client.SendEncryptedFrame(frame.data(), frame.size());
|
||||||
|
}
|
||||||
|
|
||||||
void EnterGame(EncryptedClient& channel_client, SmokeResult& result, bool json)
|
void EnterGame(EncryptedClient& channel_client, SmokeResult& result, bool json)
|
||||||
{
|
{
|
||||||
PacketCGEnterGame enter_game {};
|
PacketCGEnterGame enter_game {};
|
||||||
@@ -822,6 +870,41 @@ void EnterGame(EncryptedClient& channel_client, SmokeResult& result, bool json)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OpenMall(EncryptedClient& channel_client, const std::string& password, SmokeResult& result, bool json)
|
||||||
|
{
|
||||||
|
Expect(!password.empty(), "mall password is empty");
|
||||||
|
Expect(password.size() <= SAFEBOX_PASSWORD_MAX_LEN_LOCAL, "mall password too long");
|
||||||
|
|
||||||
|
SendChatCommand(channel_client, "/mall_password " + password);
|
||||||
|
|
||||||
|
std::vector<uint8_t> frame;
|
||||||
|
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const int timeout_ms = RemainingTimeoutMs(deadline);
|
||||||
|
Expect(timeout_ms > 0, "timed out waiting for mall open response");
|
||||||
|
Expect(channel_client.WaitForFrame(frame, timeout_ms), "timed out waiting for mall open response");
|
||||||
|
const auto header = FrameHeader(frame);
|
||||||
|
|
||||||
|
if (header == GC::SAFEBOX_WRONG_PASSWORD)
|
||||||
|
{
|
||||||
|
Expect(frame.size() == sizeof(PacketGCSafeboxWrongPassword), "unexpected safebox wrong password packet size");
|
||||||
|
throw std::runtime_error("mall password rejected");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header == GC::MALL_OPEN)
|
||||||
|
{
|
||||||
|
Expect(frame.size() == sizeof(PacketGCSafeboxSize), "unexpected mall open packet size");
|
||||||
|
const auto* mall_open = reinterpret_cast<const PacketGCSafeboxSize*>(frame.data());
|
||||||
|
result.mall_opened = true;
|
||||||
|
result.mall_size = mall_open->size;
|
||||||
|
EmitEvent(result, json, "mall_open size=" + std::to_string(static_cast<int>(mall_open->size)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
@@ -835,7 +918,7 @@ int main(int argc, char** argv)
|
|||||||
"usage: metin_login_smoke <host> <auth_port> <channel_port> <login> <password>\n"
|
"usage: metin_login_smoke <host> <auth_port> <channel_port> <login> <password>\n"
|
||||||
" or: metin_login_smoke <host> <auth_port> <channel_port> <login> --password-env=ENV_NAME\n"
|
" or: metin_login_smoke <host> <auth_port> <channel_port> <login> --password-env=ENV_NAME\n"
|
||||||
" optional: --create-character-name=NAME [--client-version=VERSION] [--json] "
|
" optional: --create-character-name=NAME [--client-version=VERSION] [--json] "
|
||||||
"[--expect-auth-failure=STATUS] [--expect-channel-failure=STATUS]");
|
"[--expect-auth-failure=STATUS] [--expect-channel-failure=STATUS] [--mall-password=PASSWORD]");
|
||||||
|
|
||||||
const std::string host = argv[1];
|
const std::string host = argv[1];
|
||||||
const uint16_t auth_port = static_cast<uint16_t>(std::stoi(argv[2]));
|
const uint16_t auth_port = static_cast<uint16_t>(std::stoi(argv[2]));
|
||||||
@@ -863,6 +946,7 @@ int main(int argc, char** argv)
|
|||||||
const std::string version_prefix = "--client-version=";
|
const std::string version_prefix = "--client-version=";
|
||||||
const std::string auth_failure_prefix = "--expect-auth-failure=";
|
const std::string auth_failure_prefix = "--expect-auth-failure=";
|
||||||
const std::string channel_failure_prefix = "--expect-channel-failure=";
|
const std::string channel_failure_prefix = "--expect-channel-failure=";
|
||||||
|
const std::string mall_password_prefix = "--mall-password=";
|
||||||
if (create_arg.rfind(create_prefix, 0) == 0)
|
if (create_arg.rfind(create_prefix, 0) == 0)
|
||||||
{
|
{
|
||||||
options.create_character_name = create_arg.substr(create_prefix.size());
|
options.create_character_name = create_arg.substr(create_prefix.size());
|
||||||
@@ -892,6 +976,14 @@ int main(int argc, char** argv)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (create_arg.rfind(mall_password_prefix, 0) == 0)
|
||||||
|
{
|
||||||
|
options.mall_password = create_arg.substr(mall_password_prefix.size());
|
||||||
|
Expect(!options.mall_password.empty(), "mall password is empty");
|
||||||
|
Expect(options.mall_password.size() <= SAFEBOX_PASSWORD_MAX_LEN_LOCAL, "mall password too long");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (create_arg == "--json")
|
if (create_arg == "--json")
|
||||||
{
|
{
|
||||||
options.json = true;
|
options.json = true;
|
||||||
@@ -1008,6 +1100,14 @@ int main(int argc, char** argv)
|
|||||||
EnterGame(channel_client, result, options.json);
|
EnterGame(channel_client, result, options.json);
|
||||||
result.entergame_ms = ElapsedMs(started_at);
|
result.entergame_ms = ElapsedMs(started_at);
|
||||||
|
|
||||||
|
if (!options.mall_password.empty())
|
||||||
|
{
|
||||||
|
result.stage = "mall_open";
|
||||||
|
started_at = std::chrono::steady_clock::now();
|
||||||
|
OpenMall(channel_client, options.mall_password, result, options.json);
|
||||||
|
result.mall_open_ms = ElapsedMs(started_at);
|
||||||
|
}
|
||||||
|
|
||||||
result.ok = true;
|
result.ok = true;
|
||||||
result.result = "success";
|
result.result = "success";
|
||||||
result.stage = "complete";
|
result.stage = "complete";
|
||||||
|
|||||||
Reference in New Issue
Block a user