Add Delete command to RPCN
Some checks are pending
Generate Translation Template / Generate Translation Template (push) Waiting to run
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (/rpcs3/.ci/build-linux-aarch64.sh, gcc, rpcs3/rpcs3-ci-jammy-aarch64:1.7, ubuntu-24.04-arm) (push) Waiting to run
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (/rpcs3/.ci/build-linux.sh, gcc, rpcs3/rpcs3-ci-jammy:1.7, ubuntu-24.04) (push) Waiting to run
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1, rpcs3/rpcs3-binaries-linux-arm64, /rpcs3/.ci/build-linux-aarch64.sh, clang, rpcs3/rpcs3-ci-jammy-aarch64:1.7, ubuntu-24.04-arm) (push) Waiting to run
Build RPCS3 / RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }} (d812f1254a1157c80fd402f94446310560f54e5f, rpcs3/rpcs3-binaries-linux, /rpcs3/.ci/build-linux.sh, clang, rpcs3/rpcs3-ci-jammy:1.7, ubuntu-24.04) (push) Waiting to run
Build RPCS3 / RPCS3 Mac ${{ matrix.name }} (0, 51ae32f468089a8169aaf1567de355ff4a3e0842, rpcs3/rpcs3-binaries-mac, Intel) (push) Waiting to run
Build RPCS3 / RPCS3 Mac ${{ matrix.name }} (1, 8e21bdbc40711a3fccd18fbf17b742348b0f4281, rpcs3/rpcs3-binaries-mac-arm64, Apple Silicon) (push) Waiting to run
Build RPCS3 / RPCS3 Windows (push) Waiting to run
Build RPCS3 / RPCS3 Windows Clang ${{ matrix.arch }} (aarch64, clang, clangarm64, ARM64, windows-11-arm) (push) Waiting to run
Build RPCS3 / RPCS3 Windows Clang ${{ matrix.arch }} (x86_64, clang, clang64, X64, windows-2025) (push) Waiting to run
Build RPCS3 / RPCS3 FreeBSD (push) Waiting to run

This commit is contained in:
RipleyTom 2026-01-12 17:29:45 +01:00 committed by Megamouse
parent ee1886fad9
commit 4a12f70f2c
5 changed files with 159 additions and 47 deletions

View file

@ -101,6 +101,7 @@ void fmt_class_string<rpcn::CommandType>::format(std::string& out, u64 arg)
case rpcn::CommandType::Login: return "Login";
case rpcn::CommandType::Terminate: return "Terminate";
case rpcn::CommandType::Create: return "Create";
case rpcn::CommandType::Delete: return "Delete";
case rpcn::CommandType::SendToken: return "SendToken";
case rpcn::CommandType::SendResetToken: return "SendResetToken";
case rpcn::CommandType::ResetPassword: return "ResetPassword";
@ -255,7 +256,7 @@ namespace rpcn
rpcn_log.notice("online: %s, pr_com_id: %s, pr_title: %s, pr_status: %s, pr_comment: %s, pr_data: %s", online ? "true" : "false", pr_com_id.data, pr_title, pr_status, pr_comment, fmt::buf_to_hexstring(pr_data.data(), pr_data.size()));
}
constexpr u32 RPCN_PROTOCOL_VERSION = 27;
constexpr u32 RPCN_PROTOCOL_VERSION = 28;
constexpr usz RPCN_HEADER_SIZE = 15;
const char* error_to_explanation(rpcn::ErrorType error)
@ -656,7 +657,7 @@ namespace rpcn
}
// Those commands are handled synchronously and won't be forwarded to NP Handler
if (command == CommandType::Login || command == CommandType::GetServerList || command == CommandType::Create ||
if (command == CommandType::Login || command == CommandType::GetServerList || command == CommandType::Create || command == CommandType::Delete ||
command == CommandType::AddFriend || command == CommandType::RemoveFriend ||
command == CommandType::AddBlock || command == CommandType::RemoveBlock ||
command == CommandType::SendMessage || command == CommandType::SendToken ||
@ -1192,7 +1193,7 @@ namespace rpcn
std::copy(token.begin(), token.end(), std::back_inserter(data));
data.push_back(0);
u64 req_id = rpcn_request_counter.fetch_add(1);
const u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
@ -1278,7 +1279,7 @@ namespace rpcn
bool rpcn_client::terminate_connection()
{
u64 req_id = rpcn_request_counter.fetch_add(1);
const u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
std::vector<u8> data;
@ -1314,7 +1315,7 @@ namespace rpcn
std::copy(email.begin(), email.end(), std::back_inserter(data));
data.push_back(0);
u64 req_id = rpcn_request_counter.fetch_add(1);
const u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
if (!forge_send_reply(CommandType::Create, req_id, data, packet_data))
@ -1348,7 +1349,7 @@ namespace rpcn
std::copy(password.begin(), password.end(), std::back_inserter(data));
data.push_back(0);
u64 req_id = rpcn_request_counter.fetch_add(1);
const u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
if (!forge_send_reply(CommandType::SendToken, req_id, data, packet_data))
@ -1381,7 +1382,7 @@ namespace rpcn
std::copy(email.begin(), email.end(), std::back_inserter(data));
data.push_back(0);
u64 req_id = rpcn_request_counter.fetch_add(1);
const u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
if (!forge_send_reply(CommandType::SendResetToken, req_id, data, packet_data))
@ -1416,7 +1417,7 @@ namespace rpcn
std::copy(password.begin(), password.end(), std::back_inserter(data));
data.push_back(0);
u64 req_id = rpcn_request_counter.fetch_add(1);
const u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
if (!forge_send_reply(CommandType::ResetPassword, req_id, data, packet_data))
@ -1435,13 +1436,43 @@ namespace rpcn
return error;
}
ErrorType rpcn_client::delete_account()
{
const auto npid = g_cfg_rpcn.get_npid();
const auto password = g_cfg_rpcn.get_password();
std::vector<u8> data;
std::copy(npid.begin(), npid.end(), std::back_inserter(data));
data.push_back(0);
std::copy(password.begin(), password.end(), std::back_inserter(data));
data.push_back(0);
const u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
if (!forge_send_reply(CommandType::Delete, req_id, data, packet_data))
{
return ErrorType::Malformed;
}
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
if (error == rpcn::ErrorType::NoError)
{
rpcn_log.success("Account was successfully deleted!");
}
return error;
}
bool rpcn_client::add_friend(const std::string& friend_username)
{
std::vector<u8> data;
std::copy(friend_username.begin(), friend_username.end(), std::back_inserter(data));
data.push_back(0);
u64 req_id = rpcn_request_counter.fetch_add(1);
const u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
if (!forge_send_reply(CommandType::AddFriend, req_id, data, packet_data))
@ -1467,7 +1498,7 @@ namespace rpcn
std::copy(friend_username.begin(), friend_username.end(), std::back_inserter(data));
data.push_back(0);
u64 req_id = rpcn_request_counter.fetch_add(1);
const u64 req_id = rpcn_request_counter.fetch_add(1);
std::vector<u8> packet_data;
if (!forge_send_reply(CommandType::RemoveFriend, req_id, data, packet_data))

View file

@ -304,6 +304,7 @@ namespace rpcn
ErrorType resend_token(const std::string& npid, const std::string& password);
ErrorType send_reset_token(std::string_view npid, std::string_view email);
ErrorType reset_password(std::string_view npid, std::string_view token, std::string_view password);
ErrorType delete_account();
bool add_friend(const std::string& friend_username);
bool remove_friend(const std::string& friend_username);

View file

@ -9,6 +9,7 @@ namespace rpcn
Login,
Terminate,
Create,
Delete,
SendToken,
SendResetToken,
ResetPassword,

View file

@ -76,6 +76,20 @@ std::string derive_password(std::string_view user_password)
return derived_password;
}
std::shared_ptr<rpcn::rpcn_client> get_rpcn_connection(QWidget* parent)
{
const auto rpcn = rpcn::rpcn_client::get_instance(0);
if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
{
const QString error_message = QObject::tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result)));
QMessageBox::critical(parent, QObject::tr("Error Connecting to RPCN!"), error_message, QMessageBox::Ok);
return nullptr;
}
return rpcn;
}
rpcn_settings_dialog::rpcn_settings_dialog(QWidget* parent)
: QDialog(parent)
{
@ -279,15 +293,11 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
return;
{
const auto rpcn = rpcn::rpcn_client::get_instance(0);
const auto avatar_url = "https://rpcs3.net/cdn/netplay/DefaultAvatar.png";
const auto rpcn = get_rpcn_connection(this);
if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
{
const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result)));
QMessageBox::critical(this, tr("Error Connecting"), error_message, QMessageBox::Ok);
if (!rpcn)
return;
}
if (auto error = rpcn->create_user(*username, *password, *username, avatar_url, *email); error != rpcn::ErrorType::NoError)
{
@ -334,14 +344,11 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
connect(btn_test, &QAbstractButton::clicked, this, [this]()
{
auto rpcn = rpcn::rpcn_client::get_instance(0);
const auto rpcn = get_rpcn_connection(this);
if (auto res = rpcn->wait_for_connection(); res != rpcn::rpcn_state::failure_no_failure)
{
const QString error_msg = tr("Failed to connect to RPCN:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(res)));
QMessageBox::warning(this, tr("Error connecting to RPCN!"), error_msg, QMessageBox::Ok);
if (!rpcn)
return;
}
if (auto res = rpcn->wait_for_authentified(); res != rpcn::rpcn_state::failure_no_failure)
{
const QString error_msg = tr("Failed to authentify to RPCN:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(res)));
@ -657,6 +664,68 @@ const std::optional<std::string>& rpcn_ask_token_dialog::get_token() const
return m_token;
}
rpcn_confirm_delete_dialog::rpcn_confirm_delete_dialog(QWidget* parent)
: QDialog(parent)
{
setWindowTitle(tr("Confirm Account Deletion"));
setObjectName("rpcn_confirm_delete_dialog");
QVBoxLayout* vbox_global = new QVBoxLayout();
QLabel* lbl_description = new QLabel(tr("Are you sure you want to delete RPCN account \"%1\"?\n\n"
"Important:\n"
"Deleting your account will blacklist your username and email for 3 months.\n"
"To confirm, type your username below and click \"Yes\".\n")
.arg(QString::fromStdString(g_cfg_rpcn.get_npid())));
QLineEdit* edit_for_delete = new QLineEdit();
edit_for_delete->setPlaceholderText(tr("Type your username to confirm"));
QPushButton* btn_yes = new QPushButton(tr("Yes"));
btn_yes->setEnabled(false);
QPushButton* btn_no = new QPushButton(tr("No"));
QDialogButtonBox* btn_box = new QDialogButtonBox(Qt::Horizontal);
btn_box->addButton(btn_yes, QDialogButtonBox::AcceptRole);
btn_box->addButton(btn_no, QDialogButtonBox::RejectRole);
vbox_global->addWidget(lbl_description);
vbox_global->addWidget(edit_for_delete);
vbox_global->addWidget(btn_box);
setLayout(vbox_global);
connect(edit_for_delete, &QLineEdit::textChanged, this, [btn_yes](const QString& text)
{
btn_yes->setEnabled(text == g_cfg_rpcn.get_npid());
});
connect(btn_box, &QDialogButtonBox::accepted, this, [this]()
{
const auto rpcn = get_rpcn_connection(this);
if (!rpcn)
return QDialog::reject();
if (auto error = rpcn->delete_account(); error != rpcn::ErrorType::NoError)
{
QString error_message;
switch (error)
{
case rpcn::ErrorType::LoginError: error_message = tr("Invalid login or password."); break;
case rpcn::ErrorType::LoginAlreadyLoggedIn: error_message = tr("Cannot delete a currently logged-in account."); break;
default: error_message = tr("An unknown error occurred."); break;
}
QMessageBox::critical(this, tr("Deletion Failed"), tr("Failed to delete the account:\n%1").arg(error_message), QMessageBox::Ok);
QDialog::reject();
return;
}
QMessageBox::information(this, tr("Account Deleted"), tr("Your account has been successfully deleted."), QMessageBox::Ok);
QDialog::accept();
});
connect(btn_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
rpcn_account_edit_dialog::rpcn_account_edit_dialog(QWidget* parent)
: QDialog(parent)
{
@ -682,6 +751,7 @@ rpcn_account_edit_dialog::rpcn_account_edit_dialog(QWidget* parent)
QPushButton* btn_resendtoken = new QPushButton(tr("Resend Token"), this);
QPushButton* btn_change_password = new QPushButton(tr("Change Password"), this);
QPushButton* btn_delete_account = new QPushButton(tr("Delete Account"), this);
QPushButton* btn_save = new QPushButton(tr("Save"), this);
vbox_labels->addWidget(lbl_username);
@ -694,6 +764,7 @@ rpcn_account_edit_dialog::rpcn_account_edit_dialog(QWidget* parent)
hbox_buttons->addWidget(btn_resendtoken);
hbox_buttons->addWidget(btn_change_password);
hbox_buttons->addWidget(btn_delete_account);
hbox_buttons->addStretch();
hbox_buttons->addWidget(btn_save);
@ -727,6 +798,7 @@ rpcn_account_edit_dialog::rpcn_account_edit_dialog(QWidget* parent)
});
connect(btn_resendtoken, &QAbstractButton::clicked, this, &rpcn_account_edit_dialog::resend_token);
connect(btn_change_password, &QAbstractButton::clicked, this, &rpcn_account_edit_dialog::change_password);
connect(btn_delete_account, &QAbstractButton::clicked, this, &rpcn_account_edit_dialog::delete_account);
g_cfg_rpcn.load();
@ -770,17 +842,13 @@ void rpcn_account_edit_dialog::resend_token()
if (!save_config())
return;
const auto rpcn = rpcn::rpcn_client::get_instance(0);
const std::string npid = g_cfg_rpcn.get_npid();
const std::string password = g_cfg_rpcn.get_password();
if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
{
const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result)));
QMessageBox::critical(this, tr("Error Connecting!"), error_message, QMessageBox::Ok);
const auto rpcn = get_rpcn_connection(this);
if (!rpcn)
return;
}
if (auto error = rpcn->resend_token(npid, password); error != rpcn::ErrorType::NoError)
{
@ -823,13 +891,10 @@ void rpcn_account_edit_dialog::change_password()
return;
{
const auto rpcn = rpcn::rpcn_client::get_instance(0);
if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
{
const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result)));
QMessageBox::critical(this, tr("Error Connecting!"), error_message, QMessageBox::Ok);
const auto rpcn = get_rpcn_connection(this);
if (!rpcn)
return;
}
if (auto error = rpcn->send_reset_token(*username, *email); error != rpcn::ErrorType::NoError)
{
@ -868,13 +933,10 @@ void rpcn_account_edit_dialog::change_password()
return;
{
const auto rpcn = rpcn::rpcn_client::get_instance(0);
if (auto result = rpcn->wait_for_connection(); result != rpcn::rpcn_state::failure_no_failure)
{
const QString error_message = tr("Failed to connect to RPCN server:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(result)));
QMessageBox::critical(this, tr("Error Connecting!"), error_message, QMessageBox::Ok);
const auto rpcn = get_rpcn_connection(this);
if (!rpcn)
return;
}
if (auto error = rpcn->reset_password(*username, *token, *password); error != rpcn::ErrorType::NoError)
{
@ -903,6 +965,17 @@ void rpcn_account_edit_dialog::change_password()
}
}
void rpcn_account_edit_dialog::delete_account()
{
if (g_cfg_rpcn.get_npid().empty() || g_cfg_rpcn.get_password().empty())
{
QMessageBox::warning(this, tr("Account Not Configured"), tr("Please configure your account in the settings before deleting it."), QMessageBox::Ok);
return;
}
rpcn_confirm_delete_dialog dlg_delete(this);
dlg_delete.exec();
}
void friend_callback(void* param, rpcn::NotificationType ntype, const std::string& username, bool status)
{
auto* dlg = static_cast<rpcn_friends_dialog*>(param);
@ -1057,14 +1130,11 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
setLayout(vbox_global);
// Tries to connect to RPCN
m_rpcn = rpcn::rpcn_client::get_instance(0);
m_rpcn = get_rpcn_connection(this);
if (auto res = m_rpcn->wait_for_connection(); res != rpcn::rpcn_state::failure_no_failure)
{
const QString error_msg = tr("Failed to connect to RPCN:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(res)));
QMessageBox::warning(parent, tr("Error connecting to RPCN!"), error_msg, QMessageBox::Ok);
if (!m_rpcn)
return;
}
if (auto res = m_rpcn->wait_for_authentified(); res != rpcn::rpcn_state::failure_no_failure)
{
const QString error_msg = tr("Failed to authentify to RPCN:\n%0").arg(QString::fromStdString(rpcn::rpcn_state_to_string(res)));

View file

@ -3,6 +3,7 @@
#include <optional>
#include <QDialog>
#include <QComboBox>
#include <QLineEdit>
#include <QListWidget>
@ -83,6 +84,13 @@ private:
std::optional<std::string> m_token;
};
class rpcn_confirm_delete_dialog : public QDialog
{
Q_OBJECT
public:
rpcn_confirm_delete_dialog(QWidget* parent);
};
class rpcn_account_edit_dialog : public QDialog
{
Q_OBJECT
@ -95,6 +103,7 @@ private:
private Q_SLOTS:
void resend_token();
void change_password();
void delete_account();
protected:
QLineEdit *m_edit_username, *m_edit_token;