tests: cover headless character delete
This commit is contained in:
@@ -155,6 +155,14 @@ struct PacketCGPlayerCreate
|
||||
uint8_t dex;
|
||||
};
|
||||
|
||||
struct PacketCGPlayerDelete
|
||||
{
|
||||
uint16_t header;
|
||||
uint16_t length;
|
||||
uint8_t index;
|
||||
char private_code[8];
|
||||
};
|
||||
|
||||
struct PacketGCPlayerCreateSuccess
|
||||
{
|
||||
uint16_t header;
|
||||
@@ -170,6 +178,13 @@ struct PacketGCCreateFailure
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
struct PacketGCPlayerDeleteSuccess
|
||||
{
|
||||
uint16_t header;
|
||||
uint16_t length;
|
||||
uint8_t account_index;
|
||||
};
|
||||
|
||||
struct PacketCGPlayerSelect
|
||||
{
|
||||
uint16_t header;
|
||||
@@ -247,6 +262,7 @@ struct SmokeOptions
|
||||
std::string client_version = "1215955205";
|
||||
std::string expect_auth_failure;
|
||||
std::string expect_channel_failure;
|
||||
std::string delete_private_code;
|
||||
std::string mall_password;
|
||||
};
|
||||
|
||||
@@ -262,14 +278,17 @@ struct SmokeResult
|
||||
int character_index = -1;
|
||||
std::string character_name;
|
||||
bool character_created = false;
|
||||
bool character_deleted = false;
|
||||
int game_channel = -1;
|
||||
bool mall_opened = false;
|
||||
int mall_size = -1;
|
||||
int deleted_character_index = -1;
|
||||
int64_t auth_handshake_ms = -1;
|
||||
int64_t auth_login_ms = -1;
|
||||
int64_t channel_handshake_ms = -1;
|
||||
int64_t channel_login_ms = -1;
|
||||
int64_t character_create_ms = -1;
|
||||
int64_t character_delete_ms = -1;
|
||||
int64_t character_select_ms = -1;
|
||||
int64_t entergame_ms = -1;
|
||||
int64_t mall_open_ms = -1;
|
||||
@@ -358,6 +377,8 @@ void PrintJson(const SmokeResult& result)
|
||||
<< ",\"character_index\":" << result.character_index
|
||||
<< ",\"character_name\":\"" << JsonEscape(result.character_name) << "\""
|
||||
<< ",\"character_created\":" << (result.character_created ? "true" : "false")
|
||||
<< ",\"character_deleted\":" << (result.character_deleted ? "true" : "false")
|
||||
<< ",\"deleted_character_index\":" << result.deleted_character_index
|
||||
<< ",\"game_channel\":" << result.game_channel
|
||||
<< ",\"mall_opened\":" << (result.mall_opened ? "true" : "false")
|
||||
<< ",\"mall_size\":" << result.mall_size
|
||||
@@ -367,6 +388,7 @@ void PrintJson(const SmokeResult& result)
|
||||
<< ",\"channel_handshake\":" << result.channel_handshake_ms
|
||||
<< ",\"channel_login\":" << result.channel_login_ms
|
||||
<< ",\"character_create\":" << result.character_create_ms
|
||||
<< ",\"character_delete\":" << result.character_delete_ms
|
||||
<< ",\"character_select\":" << result.character_select_ms
|
||||
<< ",\"entergame\":" << result.entergame_ms
|
||||
<< ",\"mall_open\":" << result.mall_open_ms
|
||||
@@ -735,6 +757,53 @@ uint8_t CreateCharacter(EncryptedClient& channel_client, std::string_view create
|
||||
throw std::runtime_error("did not receive PLAYER_CREATE_SUCCESS");
|
||||
}
|
||||
|
||||
void DeleteCharacter(EncryptedClient& channel_client, uint8_t character_index, std::string_view private_code, SmokeResult& result, bool json)
|
||||
{
|
||||
Expect(private_code.size() == 7, "delete private code must be exactly 7 characters");
|
||||
|
||||
PacketCGPlayerDelete delete_packet {};
|
||||
delete_packet.header = CG::CHARACTER_DELETE;
|
||||
delete_packet.length = sizeof(delete_packet);
|
||||
delete_packet.index = character_index;
|
||||
std::strncpy(delete_packet.private_code, private_code.data(), sizeof(delete_packet.private_code) - 1);
|
||||
channel_client.SendEncryptedPacket(delete_packet);
|
||||
|
||||
std::vector<uint8_t> frame;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
Expect(channel_client.WaitForFrame(frame, 5000), "timed out waiting for character delete response");
|
||||
const auto header = FrameHeader(frame);
|
||||
|
||||
if (header == GC::PHASE)
|
||||
continue;
|
||||
|
||||
if (header == GC::LOGIN_FAILURE)
|
||||
{
|
||||
Expect(frame.size() == sizeof(PacketGCLoginFailure), "unexpected character delete failure size");
|
||||
const auto* failure = reinterpret_cast<const PacketGCLoginFailure*>(frame.data());
|
||||
throw std::runtime_error(std::string("character delete failed: ") + failure->status);
|
||||
}
|
||||
|
||||
if (header == GC::PLAYER_DELETE_WRONG_SOCIAL_ID)
|
||||
{
|
||||
throw std::runtime_error("character delete failed: wrong private code");
|
||||
}
|
||||
|
||||
if (header == GC::PLAYER_DELETE_SUCCESS)
|
||||
{
|
||||
Expect(frame.size() == sizeof(PacketGCPlayerDeleteSuccess), "unexpected player delete success size");
|
||||
const auto* success = reinterpret_cast<const PacketGCPlayerDeleteSuccess*>(frame.data());
|
||||
Expect(success->account_index == character_index, "character delete returned unexpected index");
|
||||
result.character_deleted = true;
|
||||
result.deleted_character_index = success->account_index;
|
||||
EmitEvent(result, json, "character_deleted index=" + std::to_string(static_cast<int>(success->account_index)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("did not receive PLAYER_DELETE_SUCCESS");
|
||||
}
|
||||
|
||||
void SelectCharacter(EncryptedClient& channel_client, uint8_t character_index, SmokeResult& result, bool json)
|
||||
{
|
||||
PacketCGPlayerSelect select_packet {};
|
||||
@@ -918,7 +987,8 @@ int main(int argc, char** argv)
|
||||
"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"
|
||||
" optional: --create-character-name=NAME [--client-version=VERSION] [--json] "
|
||||
"[--expect-auth-failure=STATUS] [--expect-channel-failure=STATUS] [--mall-password=PASSWORD]");
|
||||
"[--expect-auth-failure=STATUS] [--expect-channel-failure=STATUS] [--delete-private-code=CODE] "
|
||||
"[--mall-password=PASSWORD]");
|
||||
|
||||
const std::string host = argv[1];
|
||||
const uint16_t auth_port = static_cast<uint16_t>(std::stoi(argv[2]));
|
||||
@@ -946,6 +1016,7 @@ int main(int argc, char** argv)
|
||||
const std::string version_prefix = "--client-version=";
|
||||
const std::string auth_failure_prefix = "--expect-auth-failure=";
|
||||
const std::string channel_failure_prefix = "--expect-channel-failure=";
|
||||
const std::string delete_private_code_prefix = "--delete-private-code=";
|
||||
const std::string mall_password_prefix = "--mall-password=";
|
||||
if (create_arg.rfind(create_prefix, 0) == 0)
|
||||
{
|
||||
@@ -976,6 +1047,14 @@ int main(int argc, char** argv)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (create_arg.rfind(delete_private_code_prefix, 0) == 0)
|
||||
{
|
||||
options.delete_private_code = create_arg.substr(delete_private_code_prefix.size());
|
||||
Expect(!options.delete_private_code.empty(), "delete private code is empty");
|
||||
Expect(options.delete_private_code.size() == 7, "delete private code must be exactly 7 characters");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (create_arg.rfind(mall_password_prefix, 0) == 0)
|
||||
{
|
||||
options.mall_password = create_arg.substr(mall_password_prefix.size());
|
||||
@@ -1087,6 +1166,21 @@ int main(int argc, char** argv)
|
||||
result.character_index = character_index;
|
||||
}
|
||||
|
||||
if (!options.delete_private_code.empty())
|
||||
{
|
||||
result.stage = "character_delete";
|
||||
started_at = std::chrono::steady_clock::now();
|
||||
DeleteCharacter(channel_client, character_index, options.delete_private_code, result, options.json);
|
||||
result.character_delete_ms = ElapsedMs(started_at);
|
||||
result.ok = true;
|
||||
result.result = "delete_success";
|
||||
result.stage = "complete";
|
||||
EmitEvent(result, options.json, "delete_success");
|
||||
if (options.json)
|
||||
PrintJson(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
result.stage = "character_select";
|
||||
started_at = std::chrono::steady_clock::now();
|
||||
SelectCharacter(channel_client, character_index, result, options.json);
|
||||
|
||||
Reference in New Issue
Block a user