rpcsx/rpcs3/Emu/NP/np_requests_gui.cpp
Elad 575a245f8d
IDM: Implement lock-free smart pointers (#16403)
Replaces `std::shared_pointer` with `stx::atomic_ptr` and `stx::shared_ptr`.

Notes to programmers:

* This pr kills the use of `dynamic_cast`, `std::dynamic_pointer_cast` and `std::weak_ptr` on IDM objects, possible replacement is to save the object ID on the base object, then use idm::check/get_unlocked to the destination type via the saved ID which may be null. Null pointer check is how you can tell type mismatch (as dynamic cast) or object destruction (as weak_ptr locking).
* Double-inheritance on IDM objects should be used with care, `stx::shared_ptr` does not support constant-evaluated pointer offsetting to parent/child type.
* `idm::check/get_unlocked` can now be used anywhere.

Misc fixes:
* Fixes some segfaults with RPCN with interaction with IDM.
* Fix deadlocks in access violation handler due locking recursion.
* Fixes race condition in process exit-spawn on memory containers read.
* Fix bug that theoretically can prevent RPCS3 from booting - fix `id_manager::typeinfo` comparison to compare members instead of `memcmp` which can fail spuriously on padding bytes.
* Ensure all IDM inherited types of base, either has `id_base` or `id_type` defined locally, this allows to make getters such as `idm::get_unlocked<lv2_socket, lv2_socket_raw>()` which were broken before. (requires save-states invalidation)
* Removes broken operator[] overload of `stx::shared_ptr` and `stx::single_ptr` for non-array types.
2024-12-22 20:59:48 +02:00

778 lines
23 KiB
C++

#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/lv2/sys_sync.h"
#include "Emu/Cell/Modules/cellSysutil.h"
#include "Emu/Memory/vm_ptr.h"
#include "Emu/IdManager.h"
#include "np_handler.h"
#include "np_contexts.h"
#include "np_helpers.h"
#include "np_structs_extra.h"
#include "fb_helpers.h"
LOG_CHANNEL(rpcn_log, "rpcn");
namespace np
{
std::pair<error_code, shared_ptr<matching_ctx>> gui_prelude(u32 ctx_id, vm::ptr<SceNpMatchingGUIHandler> handler, vm::ptr<void> arg)
{
auto ctx = get_matching_context(ctx_id);
if (!ctx)
return {SCE_NP_MATCHING_ERROR_CTX_NOT_FOUND, {}};
if (!ctx->busy.compare_and_swap_test(0, 1))
return {SCE_NP_MATCHING_ERROR_CTX_STILL_RUNNING, {}};
ctx->ctx_id = ctx_id;
ctx->gui_handler = handler;
ctx->gui_arg = arg;
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_COMMON_LOAD, 0);
return {CELL_OK, ctx};
}
void gui_epilog(const shared_ptr<matching_ctx>& ctx)
{
ensure(ctx->busy.compare_and_swap_test(1, 0), "Matching context wasn't busy in gui_epilog");
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_COMMON_UNLOAD, 0);
}
void np_handler::set_current_gui_ctx_id(u32 id)
{
std::lock_guard lock(gui_notifications.mutex);
gui_notifications.current_gui_ctx_id = id;
if (id == 0)
{
for (const auto& [key, notif_data] : gui_notifications.list)
{
np_memory.free(notif_data.edata.addr());
}
gui_notifications.list.clear();
}
}
void np_handler::set_gui_result(s32 event, np::event_data data)
{
std::lock_guard lock(gui_result.mutex);
if (gui_result.event)
np_memory.free(gui_result.data.addr());
gui_result.data = std::move(data);
gui_result.event = event;
}
error_code np_handler::get_matching_result(u32 ctx_id, u32 req_id, vm::ptr<void> buf, vm::ptr<u32> size, vm::ptr<s32> event)
{
std::lock_guard lock(gui_notifications.mutex);
auto ctx = get_matching_context(ctx_id);
if (!gui_notifications.current_gui_ctx_id || !ctx)
{
return SCE_NP_MATCHING_ERROR_CTX_NOT_FOUND;
}
if (!gui_notifications.list.contains(std::make_pair(ctx_id, req_id)))
{
return SCE_NP_MATCHING_ERROR_INVALID_REQ_ID;
}
const auto key = std::make_pair(ctx_id, req_id);
auto& notif = ::at32(gui_notifications.list, key);
if (event)
{
*event = notif.event;
}
if (!buf)
{
*size = notif.edata.size();
return CELL_OK;
}
const u32 final_size = std::min(static_cast<u32>(*size), notif.edata.size());
notif.edata.apply_relocations(buf.addr());
memcpy(buf.get_ptr(), notif.edata.data(), final_size);
*size = final_size;
np_memory.free(notif.edata.addr());
gui_notifications.list.erase(key);
return CELL_OK;
}
error_code np_handler::get_result_gui(vm::ptr<void> buf, vm::ptr<u32> size, vm::ptr<s32> event)
{
std::lock_guard lock(gui_result.mutex);
if (!gui_result.event)
{
return SCE_NP_MATCHING_ERROR_RESULT_NOT_FOUND;
}
if (event)
{
*event = gui_result.event;
}
if (!buf)
{
*size = gui_result.data.size();
return CELL_OK;
}
const u32 final_size = std::min(static_cast<u32>(*size), gui_result.data.size());
gui_result.data.apply_relocations(buf.addr());
memcpy(buf.get_ptr(), gui_result.data.data(), final_size);
*size = final_size;
np_memory.free(gui_result.data.addr());
gui_result.event = 0;
return CELL_OK;
}
error_code np_handler::create_room_gui(u32 ctx_id, vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpMatchingAttr> attr, vm::ptr<SceNpMatchingGUIHandler> handler, vm::ptr<void> arg)
{
const auto [error, ctx] = gui_prelude(ctx_id, handler, arg);
if (error)
return error;
for (auto cur_attr = attr; cur_attr; cur_attr = cur_attr->next)
{
extra_nps::print_SceNpMatchingAttr(cur_attr.get_ptr());
}
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
add_gui_request(req_id, ctx_id);
get_rpcn()->createjoin_room_gui(req_id, *communicationId, attr.get_ptr());
return CELL_OK;
}
bool np_handler::reply_create_room_gui(u32 req_id, std::vector<u8>& reply_data)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
ensure(!rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])), "Unexpected error in CreateRoomGUI reply");
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingRoomStatus>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to CreateRoomGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingJoinedRoomInfo_SIZE), sizeof(SceNpMatchingJoinedRoomInfo), MAX_SceNpMatchingJoinedRoomInfo_SIZE);
auto* room_info = reinterpret_cast<SceNpMatchingJoinedRoomInfo*>(edata.data());
MatchingRoomStatus_to_SceNpMatchingJoinedRoomInfo(edata, resp, room_info);
np_memory.shrink_allocation(edata.addr(), edata.size());
gui_cache.add_room(room_info->room_status.id);
gui_cache.add_member(room_info->room_status.id, room_info->room_status.members.get_ptr(), true);
set_gui_result(SCE_NP_MATCHING_GUI_EVENT_CREATE_ROOM, std::move(edata));
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_CREATE_ROOM, 0);
gui_epilog(ctx);
return true;
}
error_code np_handler::join_room_gui(u32 ctx_id, vm::ptr<SceNpRoomId> roomid, vm::ptr<SceNpMatchingGUIHandler> handler, vm::ptr<void> arg)
{
auto [error, ctx] = gui_prelude(ctx_id, handler, arg);
if (error)
return error;
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
add_gui_request(req_id, ctx_id);
get_rpcn()->join_room_gui(req_id, *roomid);
return CELL_OK;
}
bool np_handler::reply_join_room_gui(u32 req_id, std::vector<u8>& reply_data)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
{
s32 error = -1;
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::RoomMissing:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
case rpcn::ErrorType::RoomFull:
// Might also be SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN or SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED ?
error = SCE_NP_MATCHING_SERVER_ERROR_ROOM_CLOSED;
break;
case rpcn::ErrorType::RoomAlreadyJoined:
error = SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN;
break;
default:
fmt::throw_exception("Unexpected error in JoinRoomGUI reply: %d", reply_data[0]);
break;
}
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, error);
gui_epilog(ctx);
return true;
}
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingRoomStatus>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to JoinRoomGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingJoinedRoomInfo_SIZE), sizeof(SceNpMatchingJoinedRoomInfo), MAX_SceNpMatchingJoinedRoomInfo_SIZE);
auto* room_info = reinterpret_cast<SceNpMatchingJoinedRoomInfo*>(edata.data());
MatchingRoomStatus_to_SceNpMatchingJoinedRoomInfo(edata, resp, room_info);
np_memory.shrink_allocation(edata.addr(), edata.size());
extra_nps::print_SceNpMatchingJoinedRoomInfo(room_info);
gui_cache.add_room(room_info->room_status.id);
for (auto cur_member = room_info->room_status.members; cur_member; cur_member = cur_member->next)
{
gui_cache.add_member(room_info->room_status.id, cur_member.get_ptr(), true);
}
set_gui_result(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, std::move(edata));
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_JOIN_ROOM, 0);
gui_epilog(ctx);
return true;
}
error_code np_handler::leave_room_gui(u32 ctx_id, vm::cptr<SceNpRoomId> roomid)
{
auto ctx = get_matching_context(ctx_id);
if (!ctx)
return SCE_NP_MATCHING_ERROR_CTX_NOT_FOUND;
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
add_gui_request(req_id, ctx_id);
ensure(roomid);
extra_nps::print_SceNpRoomId(*roomid);
get_rpcn()->leave_room_gui(req_id, *roomid);
return not_an_error(req_id);
}
bool np_handler::reply_leave_room_gui(u32 req_id, std::vector<u8>& reply_data)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
{
s32 error = -1;
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
default:
fmt::throw_exception("Unexpected error in LeaveRoomGUI reply: %d", reply_data[0]);
break;
}
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, error);
return true;
}
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingRoomStatus>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to LeaveRoomGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingRoomStatus_SIZE), sizeof(SceNpMatchingRoomStatus), MAX_SceNpMatchingRoomStatus_SIZE);
auto* room_status = reinterpret_cast<SceNpMatchingRoomStatus*>(edata.data());
MatchingRoomStatus_to_SceNpMatchingRoomStatus(edata, resp, room_status);
np_memory.shrink_allocation(edata.addr(), edata.size());
extra_nps::print_SceNpMatchingRoomStatus(room_status);
gui_cache.del_room(room_status->id);
gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, .edata = std::move(edata)});
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE, 0);
return true;
}
error_code np_handler::get_room_list_gui(u32 ctx_id, vm::cptr<SceNpCommunicationId> communicationId, vm::ptr<SceNpMatchingReqRange> range, vm::ptr<SceNpMatchingSearchCondition> cond, vm::ptr<SceNpMatchingAttr> attr, vm::ptr<SceNpMatchingGUIHandler> handler, vm::ptr<void> arg, bool limit)
{
auto [error, ctx] = gui_prelude(ctx_id, handler, arg);
if (error)
return error;
for (auto cur_cond = cond; cur_cond; cur_cond = cur_cond->next)
{
extra_nps::print_SceNpMatchingSearchCondition(cur_cond.get_ptr());
}
for (auto cur_attr = attr; cur_attr; cur_attr = cur_attr->next)
{
extra_nps::print_SceNpMatchingAttr(cur_attr.get_ptr());
}
ctx->get_room_limit_version = limit;
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
add_gui_request(req_id, ctx_id);
ensure(range);
get_rpcn()->get_room_list_gui(req_id, *communicationId, range.get_ptr(), cond, attr);
return CELL_OK;
}
bool np_handler::reply_get_room_list_gui(u32 req_id, std::vector<u8>& reply_data)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
ensure(!rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])), "Unexpected error in GetRoomListGUI reply");
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingRoomList>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to GetRoomListGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingRoomList_SIZE), sizeof(SceNpMatchingRoomList), MAX_SceNpMatchingRoomList_SIZE);
auto* room_list = reinterpret_cast<SceNpMatchingRoomList*>(edata.data());
MatchingRoomList_to_SceNpMatchingRoomList(edata, resp, room_list);
np_memory.shrink_allocation(edata.addr(), edata.size());
extra_nps::print_SceNpMatchingRoomList(room_list);
if (ctx->get_room_limit_version)
{
set_gui_result(SCE_NP_MATCHING_GUI_EVENT_GET_ROOM_LIST_LIMIT, std::move(edata));
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_GET_ROOM_LIST_LIMIT, 0);
}
else
{
set_gui_result(SCE_NP_MATCHING_GUI_EVENT_GET_ROOM_LIST, std::move(edata));
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_GET_ROOM_LIST, 0);
}
gui_epilog(ctx);
return true;
}
error_code np_handler::set_room_search_flag_gui(u32 ctx_id, vm::ptr<SceNpLobbyId> /* lobby_id */, vm::ptr<SceNpRoomId> room_id, s32 flag)
{
auto ctx = get_matching_context(ctx_id);
if (!ctx)
return SCE_NP_MATCHING_ERROR_CTX_NOT_FOUND;
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
add_gui_request(req_id, ctx_id);
ensure(room_id);
extra_nps::print_SceNpRoomId(*room_id);
get_rpcn()->set_room_search_flag_gui(req_id, *room_id, flag);
return not_an_error(req_id);
}
bool np_handler::reply_set_room_search_flag_gui(u32 req_id, std::vector<u8>& reply_data)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
s32 error = 0;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
{
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
case rpcn::ErrorType::Unauthorized:
error = SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED;
break;
default:
fmt::throw_exception("Unexpected error in SetRoomSearchFlagGUI reply: %d", reply_data[0]);
break;
}
}
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_SET_ROOM_SEARCH_FLAG_DONE, error);
return true;
}
error_code np_handler::get_room_search_flag_gui(u32 ctx_id, vm::ptr<SceNpLobbyId> /* lobby_id */, vm::ptr<SceNpRoomId> room_id)
{
auto ctx = get_matching_context(ctx_id);
if (!ctx)
return SCE_NP_MATCHING_ERROR_CTX_NOT_FOUND;
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
add_gui_request(req_id, ctx_id);
ensure(room_id);
extra_nps::print_SceNpRoomId(*room_id);
get_rpcn()->get_room_search_flag_gui(req_id, *room_id);
return not_an_error(req_id);
}
bool np_handler::reply_get_room_search_flag_gui(u32 req_id, std::vector<u8>& reply_data)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
{
s32 error = -1;
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
default:
fmt::throw_exception("Unexpected error in GetRoomSearchFlagGUI reply: %d", reply_data[0]);
break;
}
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, error);
return true;
}
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingRoom>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to GetRoomSearchFlagGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingRoom_SIZE), sizeof(SceNpMatchingRoom), MAX_SceNpMatchingRoom_SIZE);
auto* room_info = reinterpret_cast<SceNpMatchingRoom*>(edata.data());
MatchingRoom_to_SceNpMatchingRoom(edata, resp, room_info);
np_memory.shrink_allocation(edata.addr(), edata.size());
extra_nps::print_SceNpMatchingRoom(room_info);
gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, .edata = std::move(edata)});
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE, 0);
return true;
}
error_code np_handler::set_room_info_gui(u32 ctx_id, vm::ptr<SceNpLobbyId> /* lobby_id */, vm::ptr<SceNpRoomId> room_id, vm::ptr<SceNpMatchingAttr> attr)
{
auto ctx = get_matching_context(ctx_id);
if (!ctx)
return SCE_NP_MATCHING_ERROR_CTX_NOT_FOUND;
for (auto cur_attr = attr; cur_attr; cur_attr = cur_attr->next)
{
extra_nps::print_SceNpMatchingAttr(cur_attr.get_ptr());
}
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
add_gui_request(req_id, ctx_id);
ensure(room_id && attr);
// extra_nps::print_SceNpRoomId(*room_id);
get_rpcn()->set_room_info_gui(req_id, *room_id, attr);
return not_an_error(req_id);
}
bool np_handler::reply_set_room_info_gui(u32 req_id, std::vector<u8>& reply_data)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
s32 error = 0;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
{
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
case rpcn::ErrorType::Unauthorized:
error = SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED;
break;
default:
fmt::throw_exception("Unexpected error in SetRoomInfoGUI reply: %d", reply_data[0]);
break;
}
}
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_SET_ROOM_INFO_DONE, error);
return true;
}
error_code np_handler::get_room_info_gui(u32 ctx_id, vm::ptr<SceNpLobbyId> /* lobby_id */, vm::ptr<SceNpRoomId> room_id, vm::ptr<SceNpMatchingAttr> attr)
{
auto ctx = get_matching_context(ctx_id);
if (!ctx)
return SCE_NP_MATCHING_ERROR_CTX_NOT_FOUND;
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
add_gui_request(req_id, ctx_id);
ensure(room_id && attr);
// extra_nps::print_SceNpRoomId(*room_id);
get_rpcn()->get_room_info_gui(req_id, *room_id, attr);
return not_an_error(req_id);
}
bool np_handler::reply_get_room_info_gui(u32 req_id, std::vector<u8>& reply_data)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
{
s32 error = -1;
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM;
break;
default:
fmt::throw_exception("Unexpected error in GetRoomInfoGUI reply: %d", reply_data[0]);
break;
}
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, error);
return true;
}
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingRoom>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to GetRoomInfoGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingRoom_SIZE), sizeof(SceNpMatchingRoom), MAX_SceNpMatchingRoom_SIZE);
auto* room_info = reinterpret_cast<SceNpMatchingRoom*>(edata.data());
MatchingRoom_to_SceNpMatchingRoom(edata, resp, room_info);
np_memory.shrink_allocation(edata.addr(), edata.size());
extra_nps::print_SceNpMatchingRoom(room_info);
gui_notifications.list.emplace(std::make_pair(gui_notifications.current_gui_ctx_id, req_id), gui_notification{.event = SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, .edata = std::move(edata)});
ctx->queue_callback(req_id, SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE, 0);
return true;
}
error_code np_handler::quickmatch_gui(u32 ctx_id, vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpMatchingSearchCondition> cond, s32 available_num, s32 timeout, vm::ptr<SceNpMatchingGUIHandler> handler, vm::ptr<void> arg)
{
auto [error, ctx] = gui_prelude(ctx_id, handler, arg);
if (error)
return error;
ctx->timeout = timeout;
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
add_gui_request(req_id, ctx_id);
get_rpcn()->quickmatch_gui(req_id, *communicationId, cond, available_num);
return CELL_OK;
}
bool np_handler::reply_quickmatch_gui(u32 req_id, std::vector<u8>& reply_data)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
ensure(!rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])), "Unexpected error in QuickMatchGUI reply");
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingGuiRoomId>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to QuickMatchGUI command");
SceNpRoomId room_id{};
ensure(resp->id() && resp->id()->size() == sizeof(SceNpRoomId::opt));
std::memcpy(room_id.opt, resp->id()->data(), sizeof(SceNpRoomId::opt));
const auto [_, inserted] = pending_quickmatching.insert_or_assign(room_id, ctx->ctx_id);
ensure(inserted);
// Now that the reply has been received, we start the wait for the notification
ctx->thread = std::make_unique<named_thread<std::function<void(SceNpRoomId)>>>("NP GUI Timeout Worker", [ctx, req_id, this](SceNpRoomId room_id)
{
ctx->wakey.wait(0, static_cast<atomic_wait_timeout>(ctx->timeout * 1'000'000'000));
if (thread_ctrl::state() == thread_state::aborting)
return;
{
std::lock_guard lock(this->mutex_quickmatching);
if (this->pending_quickmatching.erase(room_id) != 1)
return;
}
if (ctx->wakey == 0)
{
// Verify that the context is still valid
if (!idm::check_unlocked<matching_ctx>(ctx->ctx_id))
return;
rpcn_log.notice("QuickMatch timeout");
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
this->get_rpcn()->leave_room_gui(req_id, room_id);
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_QUICK_MATCH, SCE_NP_MATCHING_ERROR_TIMEOUT);
gui_epilog(ctx);
}
});
ctx->wakey = 0;
auto& thread = *ctx->thread;
thread(room_id);
return true;
}
error_code np_handler::searchjoin_gui(u32 ctx_id, vm::cptr<SceNpCommunicationId> communicationId, vm::cptr<SceNpMatchingSearchCondition> cond, vm::cptr<SceNpMatchingAttr> attr, vm::ptr<SceNpMatchingGUIHandler> handler, vm::ptr<void> arg)
{
auto [error, ctx] = gui_prelude(ctx_id, handler, arg);
if (error)
return error;
const u32 req_id = get_req_id(REQUEST_ID_HIGH::GUI);
add_gui_request(req_id, ctx_id);
get_rpcn()->searchjoin_gui(req_id, *communicationId, cond, attr);
return CELL_OK;
}
bool np_handler::reply_searchjoin_gui(u32 req_id, std::vector<u8>& reply_data)
{
auto ctx = take_pending_gui_request(req_id);
if (!ctx)
return true;
if (rpcn::is_error(static_cast<rpcn::ErrorType>(reply_data[0])))
{
s32 error = -1;
switch (static_cast<rpcn::ErrorType>(reply_data[0]))
{
case rpcn::ErrorType::NotFound:
error = SCE_NP_MATCHING_ERROR_SEARCH_JOIN_ROOM_NOT_FOUND;
break;
default:
fmt::throw_exception("Unexpected error in SearchJoinRoomGUI reply: %d", reply_data[0]);
break;
}
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, error);
gui_epilog(ctx);
return true;
}
vec_stream reply(reply_data, 1);
const auto* resp = reply.get_flatbuffer<MatchingSearchJoinRoomInfo>();
if (reply.is_error())
return error_and_disconnect("Malformed reply to SearchJoinRoomGUI command");
event_data edata(np_memory.allocate(MAX_SceNpMatchingSearchJoinRoomInfo_SIZE), sizeof(SceNpMatchingSearchJoinRoomInfo), MAX_SceNpMatchingSearchJoinRoomInfo_SIZE);
auto* room_info = reinterpret_cast<SceNpMatchingSearchJoinRoomInfo*>(edata.data());
MatchingSearchJoinRoomInfo_to_SceNpMatchingSearchJoinRoomInfo(edata, resp, room_info);
np_memory.shrink_allocation(edata.addr(), edata.size());
extra_nps::print_SceNpMatchingSearchJoinRoomInfo(room_info);
gui_cache.add_room(room_info->room_status.id);
for (auto cur_member = room_info->room_status.members; cur_member; cur_member = cur_member->next)
{
gui_cache.add_member(room_info->room_status.id, cur_member.get_ptr(), true);
}
set_gui_result(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, std::move(edata));
ctx->queue_gui_callback(SCE_NP_MATCHING_GUI_EVENT_SEARCH_JOIN, 0);
gui_epilog(ctx);
return true;
}
// Local cache requests
error_code np_handler::get_room_member_list_local_gui(u32 ctx_id, vm::ptr<SceNpRoomId> room_id, vm::ptr<u32> buflen, vm::ptr<void> buf)
{
auto ctx = get_matching_context(ctx_id);
if (!ctx)
return SCE_NP_MATCHING_ERROR_CTX_NOT_FOUND;
if (!room_id)
return SCE_NP_MATCHING_ERROR_ROOM_NOT_FOUND;
if (!buf)
{
error_code room_size = gui_cache.get_room_member_list(*room_id, 0, {});
if (room_size < 0)
return room_size;
*buflen = room_size;
return CELL_OK;
}
return gui_cache.get_room_member_list(*room_id, *buflen, buf);
}
} // namespace np