mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-06 09:39:55 +00:00
Add player history
This commit is contained in:
parent
c04cd2228e
commit
c589001dff
@ -1814,7 +1814,7 @@ error_code sceNpBasicGetFriendPresenceByNpId2(vm::cptr<SceNpId> npid, vm::ptr<Sc
|
||||
return nph.get_friend_presence_by_npid(*npid, pres.get_ptr());
|
||||
}
|
||||
|
||||
error_code sceNpBasicAddPlayersHistory(vm::cptr<SceNpId> npid, vm::ptr<char> description)
|
||||
error_code sceNpBasicAddPlayersHistory(vm::cptr<SceNpId> npid, vm::cptr<char> description)
|
||||
{
|
||||
sceNp.todo("sceNpBasicAddPlayersHistory(npid=*0x%x, description=*0x%x)", npid, description);
|
||||
|
||||
@ -1835,10 +1835,12 @@ error_code sceNpBasicAddPlayersHistory(vm::cptr<SceNpId> npid, vm::ptr<char> des
|
||||
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
|
||||
nph.add_player_to_history(npid.get_ptr(), description ? description.get_ptr() : nullptr);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpBasicAddPlayersHistoryAsync(vm::cptr<SceNpId> npids, u32 count, vm::ptr<char> description, vm::ptr<u32> reqId)
|
||||
error_code sceNpBasicAddPlayersHistoryAsync(vm::cptr<SceNpId> npids, u32 count, vm::cptr<char> description, vm::ptr<u32> reqId)
|
||||
{
|
||||
sceNp.todo("sceNpBasicAddPlayersHistoryAsync(npids=*0x%x, count=%d, description=*0x%x, reqId=*0x%x)", npids, count, description, reqId);
|
||||
|
||||
@ -1877,7 +1879,7 @@ error_code sceNpBasicAddPlayersHistoryAsync(vm::cptr<SceNpId> npids, u32 count,
|
||||
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
|
||||
}
|
||||
|
||||
auto req_id = nph.add_players_to_history(npids, count);
|
||||
auto req_id = nph.add_players_to_history(npids.get_ptr(), description ? description.get_ptr() : nullptr, count);
|
||||
|
||||
if (reqId)
|
||||
{
|
||||
@ -1919,8 +1921,7 @@ error_code sceNpBasicGetPlayersHistoryEntryCount(u32 options, vm::ptr<u32> count
|
||||
return SCE_NP_ERROR_ID_NOT_FOUND;
|
||||
}
|
||||
|
||||
// TODO: Check if there are players histories
|
||||
*count = 0;
|
||||
*count = nph.get_players_history_count(options);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@ -1958,6 +1959,11 @@ error_code sceNpBasicGetPlayersHistoryEntry(u32 options, u32 index, vm::ptr<SceN
|
||||
return SCE_NP_ERROR_ID_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!nph.get_player_history_entry(options, index, npid.get_ptr()))
|
||||
{
|
||||
return SCE_NP_ERROR_ID_NOT_FOUND;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
#endif
|
||||
|
||||
#include "util/asm.hpp"
|
||||
#include "util/yaml.hpp"
|
||||
|
||||
#include <span>
|
||||
|
||||
@ -55,6 +56,63 @@ LOG_CHANNEL(ticket_log, "Ticket");
|
||||
|
||||
namespace np
|
||||
{
|
||||
std::string get_players_history_path()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return fs::get_config_dir() + "config/players_history.yml";
|
||||
#else
|
||||
return fs::get_config_dir() + "players_history.yml";
|
||||
#endif
|
||||
}
|
||||
|
||||
std::map<std::string, player_history> load_players_history()
|
||||
{
|
||||
const auto parsing_error = [](std::string_view error) -> std::map<std::string, player_history>
|
||||
{
|
||||
nph_log.error("Error parsing %s: %s", get_players_history_path(), error);
|
||||
return {};
|
||||
};
|
||||
|
||||
std::map<std::string, player_history> history;
|
||||
|
||||
if (fs::file history_file{get_players_history_path(), fs::read + fs::create})
|
||||
{
|
||||
auto [yml_players_history, error] = yaml_load(history_file.to_string());
|
||||
|
||||
if (!error.empty())
|
||||
return parsing_error(error);
|
||||
|
||||
for (const auto& player : yml_players_history)
|
||||
{
|
||||
std::string username = player.first.Scalar();
|
||||
const auto& seq = player.second;
|
||||
|
||||
if (!seq.IsSequence() || seq.size() != 3)
|
||||
return parsing_error("Player history is not a proper sequence!");
|
||||
|
||||
const u64 timestamp = get_yaml_node_value<u64>(seq[0], error);
|
||||
if (!error.empty())
|
||||
return parsing_error(error);
|
||||
|
||||
std::string description = seq[1].Scalar();
|
||||
|
||||
if (!seq[2].IsSequence())
|
||||
return parsing_error("Expected communication ids sequence");
|
||||
|
||||
std::set<std::string> com_ids;
|
||||
|
||||
for (usz i = 0; i < seq[2].size(); i++)
|
||||
{
|
||||
com_ids.insert(seq[2][i].Scalar());
|
||||
}
|
||||
|
||||
history.insert(std::make_pair(std::move(username), player_history{.timestamp = timestamp, .communication_ids = std::move(com_ids), .description = std::move(description)}));
|
||||
}
|
||||
}
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
ticket::ticket(std::vector<u8>&& raw_data)
|
||||
: raw_data(raw_data)
|
||||
{
|
||||
@ -363,6 +421,13 @@ namespace np
|
||||
|
||||
np_handler::np_handler()
|
||||
{
|
||||
{
|
||||
auto history = load_players_history();
|
||||
|
||||
std::lock_guard lock(mutex_history);
|
||||
players_history = std::move(history);
|
||||
}
|
||||
|
||||
g_fxo->need<named_thread<signaling_handler>>();
|
||||
|
||||
is_connected = (g_cfg.net.net_active == np_internet_status::enabled);
|
||||
@ -1217,13 +1282,169 @@ namespace np
|
||||
return ::at32(match2_req_results, event_key);
|
||||
}
|
||||
|
||||
u32 np_handler::add_players_to_history(vm::cptr<SceNpId> /*npids*/, u32 /*count*/)
|
||||
player_history& np_handler::get_player_and_set_timestamp(const SceNpId& npid, u64 timestamp)
|
||||
{
|
||||
std::string npid_str = std::string(npid.handle.data);
|
||||
|
||||
if (!players_history.contains(npid_str))
|
||||
{
|
||||
auto [it, success] = players_history.insert(std::make_pair(std::move(npid_str), player_history{.timestamp = timestamp}));
|
||||
ensure(success);
|
||||
return it->second;
|
||||
}
|
||||
|
||||
auto& history = ::at32(players_history, npid_str);
|
||||
history.timestamp = timestamp;
|
||||
return history;
|
||||
}
|
||||
|
||||
constexpr usz MAX_HISTORY_ENTRIES = 200;
|
||||
|
||||
void np_handler::add_player_to_history(const SceNpId* npid, const char* description)
|
||||
{
|
||||
std::lock_guard lock(mutex_history);
|
||||
auto& history = get_player_and_set_timestamp(*npid, get_system_time());
|
||||
|
||||
if (description)
|
||||
history.description = description;
|
||||
|
||||
while (players_history.size() > MAX_HISTORY_ENTRIES)
|
||||
{
|
||||
auto it = std::min_element(players_history.begin(), players_history.end(), [](const auto& a, const auto& b) { return a.second.timestamp < b.second.timestamp; } );
|
||||
players_history.erase(it);
|
||||
}
|
||||
|
||||
save_players_history();
|
||||
}
|
||||
|
||||
u32 np_handler::add_players_to_history(const SceNpId* npids, const char* description, u32 count)
|
||||
{
|
||||
std::lock_guard lock(mutex_history);
|
||||
|
||||
const std::string communication_id_str = std::string(basic_handler.context.data);
|
||||
|
||||
for (u32 i = 0; i < count; i++)
|
||||
{
|
||||
auto& history = get_player_and_set_timestamp(npids[i], get_system_time());
|
||||
|
||||
if (description)
|
||||
history.description = description;
|
||||
|
||||
history.communication_ids.insert(communication_id_str);
|
||||
}
|
||||
|
||||
while (players_history.size() > MAX_HISTORY_ENTRIES)
|
||||
{
|
||||
auto it = std::min_element(players_history.begin(), players_history.end(), [](const auto& a, const auto& b) { return a.second.timestamp < b.second.timestamp; } );
|
||||
players_history.erase(it);
|
||||
}
|
||||
|
||||
save_players_history();
|
||||
|
||||
const u32 req_id = get_req_id(REQUEST_ID_HIGH::MISC);
|
||||
send_basic_event(SCE_NP_BASIC_EVENT_ADD_PLAYERS_HISTORY_RESULT, 0, req_id);
|
||||
return req_id;
|
||||
}
|
||||
|
||||
u32 np_handler::get_players_history_count(u32 options)
|
||||
{
|
||||
const bool all_history = (options == SCE_NP_BASIC_PLAYERS_HISTORY_OPTIONS_ALL);
|
||||
|
||||
std::lock_guard lock(mutex_history);
|
||||
|
||||
if (all_history)
|
||||
{
|
||||
return ::size32(players_history);
|
||||
}
|
||||
|
||||
const std::string communication_id_str = std::string(basic_handler.context.data);
|
||||
u32 count = 0;
|
||||
|
||||
for (auto it = players_history.begin(); it != players_history.end(); it++)
|
||||
{
|
||||
if (it->second.communication_ids.contains(communication_id_str))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool np_handler::get_player_history_entry(u32 options, u32 index, SceNpId* npid)
|
||||
{
|
||||
const bool all_history = (options == SCE_NP_BASIC_PLAYERS_HISTORY_OPTIONS_ALL);
|
||||
|
||||
std::lock_guard lock(mutex_history);
|
||||
|
||||
if (all_history)
|
||||
{
|
||||
auto it = players_history.begin();
|
||||
std::advance(it, index);
|
||||
|
||||
if (it != players_history.end())
|
||||
{
|
||||
string_to_npid(it->first, *npid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string communication_id_str = std::string(basic_handler.context.data);
|
||||
|
||||
for (auto it = players_history.begin(); it != players_history.end(); it++)
|
||||
{
|
||||
if (it->second.communication_ids.contains(communication_id_str))
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
string_to_npid(it->first, *npid);
|
||||
return true;
|
||||
}
|
||||
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void np_handler::save_players_history()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const std::string path_to_cfg = fs::get_config_dir() + "config/";
|
||||
if (!fs::create_path(path_to_cfg))
|
||||
{
|
||||
nph_log.error("Could not create path: %s", path_to_cfg);
|
||||
}
|
||||
#endif
|
||||
fs::file history_file(get_players_history_path(), fs::rewrite);
|
||||
if (!history_file)
|
||||
return;
|
||||
|
||||
YAML::Emitter out;
|
||||
|
||||
out << YAML::BeginMap;
|
||||
for (const auto& [player_npid, player_info] : players_history)
|
||||
{
|
||||
out << player_npid;
|
||||
out << YAML::BeginSeq;
|
||||
out << player_info.timestamp;
|
||||
out << player_info.description;
|
||||
out << YAML::BeginSeq;
|
||||
for (const auto& com_id : player_info.communication_ids)
|
||||
{
|
||||
out << com_id;
|
||||
}
|
||||
out << YAML::EndSeq;
|
||||
out << YAML::EndSeq;
|
||||
}
|
||||
out << YAML::EndMap;
|
||||
|
||||
history_file.write(out.c_str(), out.size());
|
||||
}
|
||||
|
||||
u32 np_handler::get_num_friends()
|
||||
{
|
||||
return get_rpcn()->get_num_friends();
|
||||
|
@ -40,6 +40,13 @@ namespace np
|
||||
} data;
|
||||
};
|
||||
|
||||
struct player_history
|
||||
{
|
||||
u64 timestamp;
|
||||
std::set<std::string> communication_ids;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
class ticket
|
||||
{
|
||||
public:
|
||||
@ -215,7 +222,10 @@ namespace np
|
||||
// Misc stuff
|
||||
void req_ticket(u32 version, const SceNpId* npid, const char* service_id, const u8* cookie, u32 cookie_size, const char* entitlement_id, u32 consumed_count);
|
||||
const ticket& get_ticket() const;
|
||||
u32 add_players_to_history(vm::cptr<SceNpId> npids, u32 count);
|
||||
void add_player_to_history(const SceNpId* npid, const char* description);
|
||||
u32 add_players_to_history(const SceNpId* npids, const char* description, u32 count);
|
||||
u32 get_players_history_count(u32 options);
|
||||
bool get_player_history_entry(u32 options, u32 index, SceNpId* npid);
|
||||
bool abort_request(u32 req_id);
|
||||
|
||||
// For signaling
|
||||
@ -346,6 +356,7 @@ namespace np
|
||||
u32 generate_callback_info(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, SceNpMatching2Event event_type);
|
||||
std::optional<callback_info> take_pending_request(u32 req_id);
|
||||
|
||||
private:
|
||||
shared_mutex mutex_pending_requests;
|
||||
std::unordered_map<u32, callback_info> pending_requests;
|
||||
shared_mutex mutex_pending_sign_infos_requests;
|
||||
@ -356,7 +367,6 @@ namespace np
|
||||
|
||||
bool m_inited_np_handler_dependencies = false;
|
||||
|
||||
private:
|
||||
// Basic event handler;
|
||||
struct
|
||||
{
|
||||
@ -441,5 +451,11 @@ namespace np
|
||||
std::string pr_comment;
|
||||
std::vector<u8> pr_data;
|
||||
} presence_self;
|
||||
|
||||
player_history& get_player_and_set_timestamp(const SceNpId& npid, u64 timestamp);
|
||||
void save_players_history();
|
||||
|
||||
shared_mutex mutex_history;
|
||||
std::map<std::string, player_history> players_history; // npid / history
|
||||
};
|
||||
} // namespace np
|
||||
|
@ -884,6 +884,19 @@ void friend_callback(void* param, rpcn::NotificationType ntype, const std::strin
|
||||
dlg->callback_handler(ntype, username, status);
|
||||
}
|
||||
|
||||
// Avoid including np_handler.h
|
||||
namespace np
|
||||
{
|
||||
struct player_history
|
||||
{
|
||||
u64 timestamp;
|
||||
std::set<std::string> communication_ids;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
std::map<std::string, player_history> load_players_history();
|
||||
}
|
||||
|
||||
rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
|
||||
: QDialog(parent),
|
||||
m_green_icon(gui::utils::circle_pixmap(QColorConstants::Svg::green, devicePixelRatioF() * 2)),
|
||||
@ -944,6 +957,14 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
|
||||
grp_list_blocks->setLayout(vbox_lst_blocks);
|
||||
hbox_groupboxes->addWidget(grp_list_blocks);
|
||||
|
||||
QGroupBox* grp_list_history = new QGroupBox(tr("Recent Players"));
|
||||
QVBoxLayout* vbox_lst_history = new QVBoxLayout();
|
||||
m_lst_history = new QListWidget(this);
|
||||
m_lst_history->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
vbox_lst_history->addWidget(m_lst_history);
|
||||
grp_list_history->setLayout(vbox_lst_history);
|
||||
hbox_groupboxes->addWidget(grp_list_history);
|
||||
|
||||
vbox_global->addLayout(hbox_groupboxes);
|
||||
|
||||
setLayout(vbox_global);
|
||||
@ -990,6 +1011,20 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
|
||||
add_update_list(m_lst_blocks, QString::fromStdString(blck), m_red_icon, QVariant(false));
|
||||
}
|
||||
|
||||
auto history = np::load_players_history();
|
||||
std::map<u64, std::string, std::greater<u64>> sorted_history;
|
||||
|
||||
for (const auto& [username, user_info] : history)
|
||||
{
|
||||
if (!data.friends.contains(username) && !data.requests_sent.contains(username) && !data.requests_received.contains(username))
|
||||
sorted_history.insert(std::make_pair(user_info.timestamp, std::move(username)));
|
||||
}
|
||||
|
||||
for (const auto& [_, username] : sorted_history)
|
||||
{
|
||||
m_lst_history->addItem(new QListWidgetItem(QString::fromStdString(username)));
|
||||
}
|
||||
|
||||
connect(this, &rpcn_friends_dialog::signal_add_update_friend, this, &rpcn_friends_dialog::add_update_friend);
|
||||
connect(this, &rpcn_friends_dialog::signal_remove_friend, this, &rpcn_friends_dialog::remove_friend);
|
||||
connect(this, &rpcn_friends_dialog::signal_add_query, this, &rpcn_friends_dialog::add_query);
|
||||
@ -1041,9 +1076,9 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
|
||||
std::string str_sel_friend = selected_item->text().toStdString();
|
||||
|
||||
QMenu* context_menu = new QMenu();
|
||||
QAction* remove_friend_action = context_menu->addAction(tr("&Accept Request"));
|
||||
QAction* accept_request_action = context_menu->addAction(tr("&Accept Request"));
|
||||
|
||||
connect(remove_friend_action, &QAction::triggered, this, [this, str_sel_friend]()
|
||||
connect(accept_request_action, &QAction::triggered, this, [this, str_sel_friend]()
|
||||
{
|
||||
if (!m_rpcn->add_friend(str_sel_friend))
|
||||
{
|
||||
@ -1059,6 +1094,38 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
|
||||
context_menu->deleteLater();
|
||||
});
|
||||
|
||||
connect(m_lst_history, &QListWidget::customContextMenuRequested, this, [this](const QPoint& pos)
|
||||
{
|
||||
if (!m_lst_history->itemAt(pos) || m_lst_history->selectedItems().count() != 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QListWidgetItem* selected_item = m_lst_history->selectedItems().first();
|
||||
|
||||
std::string str_sel_friend = selected_item->text().toStdString();
|
||||
|
||||
QMenu* context_menu = new QMenu();
|
||||
QAction* send_friend_request_action = context_menu->addAction(tr("&Send Friend Request"));
|
||||
|
||||
connect(send_friend_request_action, &QAction::triggered, this, [this, str_sel_friend]()
|
||||
{
|
||||
if (!m_rpcn->add_friend(str_sel_friend))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error sending a friend request!"), tr("An error occurred while trying to send a friend request!"), QMessageBox::Ok);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString qstr_friend = QString::fromStdString(str_sel_friend);
|
||||
add_update_list(m_lst_requests, qstr_friend, m_orange_icon, QVariant(false));
|
||||
remove_list(m_lst_history, qstr_friend);
|
||||
}
|
||||
});
|
||||
|
||||
context_menu->exec(m_lst_history->viewport()->mapToGlobal(pos));
|
||||
context_menu->deleteLater();
|
||||
});
|
||||
|
||||
connect(btn_addfriend, &QAbstractButton::clicked, this, [this]()
|
||||
{
|
||||
std::string str_friend_username;
|
||||
@ -1146,6 +1213,7 @@ void rpcn_friends_dialog::remove_friend(QString name)
|
||||
void rpcn_friends_dialog::add_query(QString name)
|
||||
{
|
||||
add_update_list(m_lst_requests, name, m_yellow_icon, QVariant(true));
|
||||
remove_list(m_lst_history, name);
|
||||
}
|
||||
|
||||
void rpcn_friends_dialog::callback_handler(rpcn::NotificationType ntype, std::string username, bool status)
|
||||
|
@ -131,6 +131,8 @@ private:
|
||||
QListWidget* m_lst_requests = nullptr;
|
||||
// list of people blocked by the user
|
||||
QListWidget* m_lst_blocks = nullptr;
|
||||
// list of players in history
|
||||
QListWidget* m_lst_history = nullptr;
|
||||
|
||||
std::shared_ptr<rpcn::rpcn_client> m_rpcn;
|
||||
bool m_rpcn_ok = false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user