From 26d406fec3eca124b157be066fa5b3057561fccb Mon Sep 17 00:00:00 2001
From: RipleyTom <RipleyTom@users.noreply.github.com>
Date: Wed, 31 Jan 2024 05:25:26 +0100
Subject: [PATCH] RPCN 1.1

---
 rpcs3/Emu/Cell/Modules/sceNp.cpp              |  12 +-
 rpcs3/Emu/Cell/Modules/sceNp2.cpp             |   2 +-
 rpcs3/Emu/Cell/Modules/sceNpPlus.cpp          |   2 +-
 rpcs3/Emu/Cell/lv2/sys_net.cpp                |   8 +-
 rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp |   5 +
 .../Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp  |   5 +
 .../Emu/Cell/lv2/sys_net/network_context.cpp  |   3 +
 rpcs3/Emu/NP/np_handler.cpp                   |   3 +-
 rpcs3/Emu/NP/np_handler.h                     |   1 +
 rpcs3/Emu/NP/np_notifications.cpp             |  20 +++
 rpcs3/Emu/NP/rpcn_client.cpp                  |   2 +-
 rpcs3/Emu/NP/rpcn_client.h                    |   1 +
 rpcs3/Emu/NP/signaling_handler.cpp            | 128 +++++++++++++-----
 rpcs3/Emu/NP/signaling_handler.h              |  18 ++-
 14 files changed, 155 insertions(+), 55 deletions(-)

diff --git a/rpcs3/Emu/Cell/Modules/sceNp.cpp b/rpcs3/Emu/Cell/Modules/sceNp.cpp
index 2941b47071..16cec30ff9 100644
--- a/rpcs3/Emu/Cell/Modules/sceNp.cpp
+++ b/rpcs3/Emu/Cell/Modules/sceNp.cpp
@@ -1860,7 +1860,7 @@ error_code sceNpBasicGetFriendPresenceByNpId2(vm::cptr<SceNpId> npid, vm::ptr<Sc
 
 error_code sceNpBasicAddPlayersHistory(vm::cptr<SceNpId> npid, vm::cptr<char> description)
 {
-	sceNp.todo("sceNpBasicAddPlayersHistory(npid=*0x%x, description=*0x%x)", npid, description);
+	sceNp.warning("sceNpBasicAddPlayersHistory(npid=*0x%x, description=*0x%x)", npid, description);
 
 	auto& nph = g_fxo->get<named_thread<np::np_handler>>();
 
@@ -1886,7 +1886,7 @@ error_code sceNpBasicAddPlayersHistory(vm::cptr<SceNpId> npid, vm::cptr<char> de
 
 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);
+	sceNp.warning("sceNpBasicAddPlayersHistoryAsync(npids=*0x%x, count=%d, description=*0x%x, reqId=*0x%x)", npids, count, description, reqId);
 
 	auto& nph = g_fxo->get<named_thread<np::np_handler>>();
 
@@ -6749,7 +6749,7 @@ error_code sceNpSignalingActivateConnection(u32 ctx_id, vm::ptr<SceNpId> npId, v
 
 error_code sceNpSignalingDeactivateConnection(u32 ctx_id, u32 conn_id)
 {
-	sceNp.todo("sceNpSignalingDeactivateConnection(ctx_id=%d, conn_id=%d)", ctx_id, conn_id);
+	sceNp.warning("sceNpSignalingDeactivateConnection(ctx_id=%d, conn_id=%d)", ctx_id, conn_id);
 
 	auto& nph = g_fxo->get<named_thread<np::np_handler>>();
 
@@ -6758,6 +6758,10 @@ error_code sceNpSignalingDeactivateConnection(u32 ctx_id, u32 conn_id)
 		return SCE_NP_SIGNALING_ERROR_NOT_INITIALIZED;
 	}
 
+	auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
+
+	sigh.stop_sig(conn_id, true);
+
 	return CELL_OK;
 }
 
@@ -6774,7 +6778,7 @@ error_code sceNpSignalingTerminateConnection(u32 ctx_id, u32 conn_id)
 
 	auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
 
-	sigh.stop_sig(conn_id);
+	sigh.stop_sig(conn_id, false);
 
 	return CELL_OK;
 }
diff --git a/rpcs3/Emu/Cell/Modules/sceNp2.cpp b/rpcs3/Emu/Cell/Modules/sceNp2.cpp
index a10691d0b7..117d50e3bd 100644
--- a/rpcs3/Emu/Cell/Modules/sceNp2.cpp
+++ b/rpcs3/Emu/Cell/Modules/sceNp2.cpp
@@ -1664,7 +1664,7 @@ error_code sceNpMatching2SendLobbyInvitation(
 
 error_code sceNpMatching2ContextStop(SceNpMatching2ContextId ctxId)
 {
-	sceNp2.todo("sceNpMatching2ContextStop(ctxId=%d)", ctxId);
+	sceNp2.warning("sceNpMatching2ContextStop(ctxId=%d)", ctxId);
 
 	auto& nph = g_fxo->get<named_thread<np::np_handler>>();
 
diff --git a/rpcs3/Emu/Cell/Modules/sceNpPlus.cpp b/rpcs3/Emu/Cell/Modules/sceNpPlus.cpp
index 6f8874b2d6..70b3f5181b 100644
--- a/rpcs3/Emu/Cell/Modules/sceNpPlus.cpp
+++ b/rpcs3/Emu/Cell/Modules/sceNpPlus.cpp
@@ -8,7 +8,7 @@ error_code sceNpManagerIsSP()
 {
 	sceNpPlus.warning("sceNpManagerIsSP()");
 	// TODO seems to be cut to 1 byte by pshome likely a bool but may be more.
-	return CELL_OK;
+	return not_an_error(1);
 }
 
 DECLARE(ppu_module_manager::sceNpPlus)("sceNpPlus", []()
diff --git a/rpcs3/Emu/Cell/lv2/sys_net.cpp b/rpcs3/Emu/Cell/lv2/sys_net.cpp
index ff563f5d8a..01ab791d42 100644
--- a/rpcs3/Emu/Cell/lv2/sys_net.cpp
+++ b/rpcs3/Emu/Cell/lv2/sys_net.cpp
@@ -770,7 +770,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32
 {
 	ppu.state += cpu_flag::wait;
 
-	sys_net.warning("sys_net_bnet_recvfrom(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, paddrlen=*0x%x)", s, buf, len, flags, addr, paddrlen);
+	sys_net.trace("sys_net_bnet_recvfrom(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, paddrlen=*0x%x)", s, buf, len, flags, addr, paddrlen);
 
 	// If addr is null, paddrlen must be null as well
 	if (!buf || !len || addr.operator bool() != paddrlen.operator bool())
@@ -984,7 +984,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
 {
 	ppu.state += cpu_flag::wait;
 
-	sys_net.warning("sys_net_bnet_sendto(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, addrlen=%u)", s, buf, len, flags, addr, addrlen);
+	sys_net.trace("sys_net_bnet_sendto(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, addrlen=%u)", s, buf, len, flags, addr, addrlen);
 
 	if (flags & ~(SYS_NET_MSG_DONTWAIT | SYS_NET_MSG_WAITALL | SYS_NET_MSG_USECRYPTO | SYS_NET_MSG_USESIGNATURE))
 	{
@@ -1256,7 +1256,7 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
 {
 	ppu.state += cpu_flag::wait;
 
-	sys_net.warning("sys_net_bnet_poll(fds=*0x%x, nfds=%d, ms=%d)", fds, nfds, ms);
+	sys_net.trace("sys_net_bnet_poll(fds=*0x%x, nfds=%d, ms=%d)", fds, nfds, ms);
 
 	if (nfds <= 0)
 	{
@@ -1438,7 +1438,7 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
 {
 	ppu.state += cpu_flag::wait;
 
-	sys_net.warning("sys_net_bnet_select(nfds=%d, readfds=*0x%x, writefds=*0x%x, exceptfds=*0x%x, timeout=*0x%x)", nfds, readfds, writefds, exceptfds, _timeout);
+	sys_net.trace("sys_net_bnet_select(nfds=%d, readfds=*0x%x, writefds=*0x%x, exceptfds=*0x%x, timeout=*0x%x)", nfds, readfds, writefds, exceptfds, _timeout);
 
 	atomic_t<s32> signaled{0};
 
diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp
index 6c7df86c09..b77d08773a 100644
--- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp
+++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2p.cpp
@@ -9,6 +9,11 @@ LOG_CHANNEL(sys_net);
 lv2_socket_p2p::lv2_socket_p2p(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol)
 	: lv2_socket(family, type, protocol)
 {
+	sockopt_cache cache_type;
+	cache_type.data._int = SYS_NET_SOCK_DGRAM_P2P;
+	cache_type.len = 4;
+
+	sockopts[(static_cast<u64>(SYS_NET_SOL_SOCKET) << 32ull) | SYS_NET_SO_TYPE] = cache_type;
 }
 
 lv2_socket_p2p::lv2_socket_p2p(utils::serial& ar, lv2_socket_type type)
diff --git a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp
index 4474164ed5..4553d73614 100644
--- a/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp
+++ b/rpcs3/Emu/Cell/lv2/sys_net/lv2_socket_p2ps.cpp
@@ -243,6 +243,11 @@ std::vector<u8> generate_u2s_packet(const p2ps_encapsulated_tcp& header, const u
 lv2_socket_p2ps::lv2_socket_p2ps(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol)
 	: lv2_socket_p2p(family, type, protocol)
 {
+	sockopt_cache cache_type;
+	cache_type.data._int = SYS_NET_SOCK_STREAM_P2P;
+	cache_type.len = 4;
+
+	sockopts[(static_cast<u64>(SYS_NET_SOL_SOCKET) << 32ull) | SYS_NET_SO_TYPE] = cache_type;
 }
 
 lv2_socket_p2ps::lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq, s32 so_nbio)
diff --git a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp
index e3d5cdda4a..6c284a14e6 100644
--- a/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp
+++ b/rpcs3/Emu/Cell/lv2/sys_net/network_context.cpp
@@ -19,6 +19,9 @@ s32 send_packet_from_p2p_port(const std::vector<u8>& data, const sockaddr_in& ad
 		{
 			auto& def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT);
 			res            = ::sendto(def_port.p2p_socket, reinterpret_cast<const char*>(data.data()), ::size32(data), 0, reinterpret_cast<const sockaddr*>(&addr), sizeof(sockaddr_in));
+
+			if (res == -1)
+				sys_net.error("Failed to send signaling packet: %s", get_last_error(false, false));
 		}
 		else
 		{
diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp
index 51a13f2923..b5bee8750f 100644
--- a/rpcs3/Emu/NP/np_handler.cpp
+++ b/rpcs3/Emu/NP/np_handler.cpp
@@ -1122,6 +1122,7 @@ namespace np
 					case rpcn::NotificationType::UpdatedRoomMemberDataInternal: notif_updated_room_member_data_internal(notif.second); break;
 					case rpcn::NotificationType::SignalP2PConnect: notif_p2p_connect(notif.second); break;
 					case rpcn::NotificationType::RoomMessageReceived: notif_room_message_received(notif.second); break;
+					case rpcn::NotificationType::SignalingInfo: notif_signaling_info(notif.second); break;
 					default: rpcn_log.error("Unknown notification(%d) received!", notif.first); break;
 					}
 				}
@@ -1240,7 +1241,7 @@ namespace np
 		ret.cb     = (optParam && optParam->cbFunc) ? optParam->cbFunc : ctx->default_match2_optparam.cbFunc;
 		ret.event_type = event_type;
 
-		nph_log.warning("Callback used is 0x%x", ret.cb);
+		nph_log.trace("Callback used is 0x%x with req_id %d", ret.cb, req_id);
 
 		{
 			std::lock_guard lock(mutex_pending_requests);
diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h
index 085dfa522e..bc83f26440 100644
--- a/rpcs3/Emu/NP/np_handler.h
+++ b/rpcs3/Emu/NP/np_handler.h
@@ -268,6 +268,7 @@ namespace np
 		void notif_updated_room_data_internal(std::vector<u8>& data);
 		void notif_updated_room_member_data_internal(std::vector<u8>& data);
 		void notif_p2p_connect(std::vector<u8>& data);
+		void notif_signaling_info(std::vector<u8>& data);
 		void notif_room_message_received(std::vector<u8>& data);
 
 		// Reply handlers
diff --git a/rpcs3/Emu/NP/np_notifications.cpp b/rpcs3/Emu/NP/np_notifications.cpp
index 14db3a6b8d..f064cf1c5d 100644
--- a/rpcs3/Emu/NP/np_notifications.cpp
+++ b/rpcs3/Emu/NP/np_notifications.cpp
@@ -241,4 +241,24 @@ namespace np
 		const u32 conn_id = sigh.init_sig2(*npid, room_id, member_id);
 		sigh.start_sig(conn_id, addr_p2p, port_p2p);
 	}
+
+	void np_handler::notif_signaling_info(std::vector<u8>& data)
+	{
+		vec_stream noti(data);
+		const u32 addr_p2p = noti.get<u32>();
+		const u32 port_p2p = noti.get<u16>();
+		const std::string str_npid = noti.get_string(false);
+
+		if (noti.is_error())
+		{
+			rpcn_log.error("Received faulty SignalingInfo notification");
+			return;
+		}
+
+		SceNpId npid_p2p;
+		string_to_npid(str_npid, npid_p2p);
+
+		auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
+		sigh.send_information_packets(addr_p2p, port_p2p, npid_p2p);
+	}
 } // namespace np
diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp
index 187f73542a..c42845ba9f 100644
--- a/rpcs3/Emu/NP/rpcn_client.cpp
+++ b/rpcs3/Emu/NP/rpcn_client.cpp
@@ -84,7 +84,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 = 22;
+	constexpr u32 RPCN_PROTOCOL_VERSION = 23;
 	constexpr usz RPCN_HEADER_SIZE      = 15;
 
 	bool is_error(ErrorType err)
diff --git a/rpcs3/Emu/NP/rpcn_client.h b/rpcs3/Emu/NP/rpcn_client.h
index 082a3bc60f..af432d787a 100644
--- a/rpcs3/Emu/NP/rpcn_client.h
+++ b/rpcs3/Emu/NP/rpcn_client.h
@@ -241,6 +241,7 @@ namespace rpcn
 		RoomMessageReceived,
 		MessageReceived,
 		FriendPresenceChanged,
+		SignalingInfo,
 	};
 
 	enum class rpcn_state
diff --git a/rpcs3/Emu/NP/signaling_handler.cpp b/rpcs3/Emu/NP/signaling_handler.cpp
index cfb4aa153d..bb1e8fb9a4 100644
--- a/rpcs3/Emu/NP/signaling_handler.cpp
+++ b/rpcs3/Emu/NP/signaling_handler.cpp
@@ -17,7 +17,6 @@
 
 LOG_CHANNEL(sign_log, "Signaling");
 
-
 template <>
 void fmt_class_string<SignalingCommand>::format(std::string& out, u64 arg)
 {
@@ -32,6 +31,7 @@ void fmt_class_string<SignalingCommand>::format(std::string& out, u64 arg)
 			case signal_confirm: return "CONFIRM";
 			case signal_finished: return "FINISHED";
 			case signal_finished_ack: return "FINISHED_ACK";
+			case signal_info: return "INFO";
 			}
 
 			return unknown;
@@ -70,42 +70,42 @@ void signaling_handler::set_sig2_cb(u16 sig2_cb_ctx, vm::ptr<SceNpMatching2Signa
 	this->sig2_cb_arg = sig2_cb_arg;
 }
 
-void signaling_handler::signal_sig_callback(u32 conn_id, int event)
+void signaling_handler::signal_sig_callback(u32 conn_id, int event, int error_code)
 {
 	if (sig_cb)
 	{
-		sysutil_register_cb([sig_cb = this->sig_cb, sig_cb_ctx = this->sig_cb_ctx, conn_id, event, sig_cb_arg = this->sig_cb_arg](ppu_thread& cb_ppu) -> s32
+		sysutil_register_cb([sig_cb = this->sig_cb, sig_cb_ctx = this->sig_cb_ctx, conn_id, event, error_code, sig_cb_arg = this->sig_cb_arg](ppu_thread& cb_ppu) -> s32
 			{
-				sig_cb(cb_ppu, sig_cb_ctx, conn_id, event, 0, sig_cb_arg);
+				sig_cb(cb_ppu, sig_cb_ctx, conn_id, event, error_code, sig_cb_arg);
 				return 0;
 			});
 		sign_log.notice("Called sig CB: 0x%x (conn_id: %d)", event, conn_id);
 	}
 
 	// extended callback also receives normal events
-	signal_ext_sig_callback(conn_id, event);
+	signal_ext_sig_callback(conn_id, event, error_code);
 }
 
-void signaling_handler::signal_ext_sig_callback(u32 conn_id, int event) const
+void signaling_handler::signal_ext_sig_callback(u32 conn_id, int event, int error_code) const
 {
 	if (sig_ext_cb)
 	{
-		sysutil_register_cb([sig_ext_cb = this->sig_ext_cb, sig_ext_cb_ctx = this->sig_ext_cb_ctx, conn_id, event, sig_ext_cb_arg = this->sig_ext_cb_arg](ppu_thread& cb_ppu) -> s32
+		sysutil_register_cb([sig_ext_cb = this->sig_ext_cb, sig_ext_cb_ctx = this->sig_ext_cb_ctx, conn_id, event, error_code, sig_ext_cb_arg = this->sig_ext_cb_arg](ppu_thread& cb_ppu) -> s32
 			{
-				sig_ext_cb(cb_ppu, sig_ext_cb_ctx, conn_id, event, 0, sig_ext_cb_arg);
+				sig_ext_cb(cb_ppu, sig_ext_cb_ctx, conn_id, event, error_code, sig_ext_cb_arg);
 				return 0;
 			});
 		sign_log.notice("Called EXT sig CB: 0x%x (conn_id: %d)", event, conn_id);
 	}
 }
 
-void signaling_handler::signal_sig2_callback(u64 room_id, u16 member_id, SceNpMatching2Event event) const
+void signaling_handler::signal_sig2_callback(u64 room_id, u16 member_id, SceNpMatching2Event event, int error_code) const
 {
 	if (room_id && sig2_cb)
 	{
-		sysutil_register_cb([sig2_cb = this->sig2_cb, sig2_cb_ctx = this->sig2_cb_ctx, room_id, member_id, event, sig2_cb_arg = this->sig2_cb_arg](ppu_thread& cb_ppu) -> s32
+		sysutil_register_cb([sig2_cb = this->sig2_cb, sig2_cb_ctx = this->sig2_cb_ctx, room_id, member_id, event, error_code, sig2_cb_arg = this->sig2_cb_arg](ppu_thread& cb_ppu) -> s32
 			{
-				sig2_cb(cb_ppu, sig2_cb_ctx, room_id, member_id, event, 0, sig2_cb_arg);
+				sig2_cb(cb_ppu, sig2_cb_ctx, room_id, member_id, event, error_code, sig2_cb_arg);
 				return 0;
 			});
 		sign_log.notice("Called sig2 CB: 0x%x (room_id: %d, member_id: %d)", event, room_id, member_id);
@@ -223,7 +223,7 @@ void signaling_handler::process_incoming_messages()
 		// Get signaling info for user to know if we should even bother looking further
 		auto si = get_signaling_ptr(sp);
 
-		if (!si && sp->command == signal_connect)
+		if (!si && (sp->command == signal_connect || sp->command == signal_info))
 		{
 			// Connection can be remotely established and not mutual
 			const u32 conn_id = get_always_conn_id(sp->npid);
@@ -288,6 +288,11 @@ void signaling_handler::process_incoming_messages()
 			schedule_repeat = false;
 			reschedule_packet(si, signal_ping, now + 10s);
 			break;
+		case signal_info:
+			update_si_addr(si, op_addr, op_port);
+			reply = false;
+			schedule_repeat = false;
+			break;
 		case signal_connect:
 			reply = true;
 			schedule_repeat = true;
@@ -306,7 +311,7 @@ void signaling_handler::process_incoming_messages()
 			retire_packet(si, signal_connect);
 			update_si_addr(si, op_addr, op_port);
 			update_si_mapped_addr(si, sp->sent_addr, sp->sent_port);
-			update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE);
+			update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_ACTIVE, CELL_OK);
 			break;
 		case signal_confirm:
 			update_rtt(sp->timestamp_receiver);
@@ -327,7 +332,7 @@ void signaling_handler::process_incoming_messages()
 		case signal_finished_ack:
 			reply = false;
 			schedule_repeat = false;
-			update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE);
+			update_si_status(si, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE, SCE_NP_SIGNALING_ERROR_TERMINATED_BY_MYSELF);
 			retire_packet(si, signal_finished);
 			break;
 		default: sign_log.error("Invalid signaling command received"); continue;
@@ -371,12 +376,15 @@ void signaling_handler::operator()()
 			if (timestamp > now)
 				break;
 
-			if (sig.sig_info->time_last_msg_recvd < now - 60s)
+			SignalingCommand cmd = sig.packet.command;
+
+			if (sig.sig_info->time_last_msg_recvd < now - 60s && cmd != signal_info)
 			{
 				// We had no connection to opponent for 60 seconds, consider the connection dead
 				sign_log.notice("Timeout disconnection");
-				update_si_status(sig.sig_info, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE);
-				break; // qpackets will be emptied of all packets for this user so we're requeuing
+				update_si_status(sig.sig_info, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE, SCE_NP_SIGNALING_ERROR_TIMEOUT);
+				retire_packet(sig.sig_info, signal_ping); // Retire ping packet if necessary
+				break; // qpackets has been emptied of all packets for this user so we're requeuing
 			}
 
 			// Update the timestamp if necessary
@@ -397,7 +405,6 @@ void signaling_handler::operator()()
 			send_signaling_packet(sig.packet, sig.sig_info->addr, sig.sig_info->port);
 
 			// Reschedule another packet
-			SignalingCommand cmd = sig.packet.command;
 			auto& si = sig.sig_info;
 
 			std::chrono::milliseconds delay(500);
@@ -416,9 +423,21 @@ void signaling_handler::operator()()
 			case signal_finished_ack:
 				delay = REPEAT_FINISHED_DELAY;
 				break;
+			case signal_info:
+				// Don't reschedule
+				if (si->info_counter == 0)
+				{
+					it = qpackets.erase(it);
+					continue;
+				}
+
+				delay = REPEAT_INFO_DELAY;
+				si->info_counter--;
+				break;
 			}
 
 			it++;
+
 			reschedule_packet(si, cmd, now + delay);
 		}
 
@@ -505,7 +524,7 @@ void signaling_handler::update_si_mapped_addr(std::shared_ptr<signaling_info>& s
 	}
 }
 
-void signaling_handler::update_si_status(std::shared_ptr<signaling_info>& si, s32 new_status)
+void signaling_handler::update_si_status(std::shared_ptr<signaling_info>& si, s32 new_status, int error_code)
 {
 	if (!si)
 		return;
@@ -514,17 +533,17 @@ void signaling_handler::update_si_status(std::shared_ptr<signaling_info>& si, s3
 	{
 		si->conn_status = SCE_NP_SIGNALING_CONN_STATUS_ACTIVE;
 
-		signal_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_ESTABLISHED);
-		signal_sig2_callback(si->room_id, si->member_id, SCE_NP_MATCHING2_SIGNALING_EVENT_Established);
+		signal_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_ESTABLISHED, error_code);
+		signal_sig2_callback(si->room_id, si->member_id, SCE_NP_MATCHING2_SIGNALING_EVENT_Established, error_code);
 
 		if (si->op_activated)
-			signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_MUTUAL_ACTIVATED);
+			signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_MUTUAL_ACTIVATED, CELL_OK);
 	}
 	else if ((si->conn_status == SCE_NP_SIGNALING_CONN_STATUS_PENDING || si->conn_status == SCE_NP_SIGNALING_CONN_STATUS_ACTIVE) && new_status == SCE_NP_SIGNALING_CONN_STATUS_INACTIVE)
 	{
 		si->conn_status = SCE_NP_SIGNALING_CONN_STATUS_INACTIVE;
-		signal_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_DEAD);
-		signal_sig2_callback(si->room_id, si->member_id, SCE_NP_MATCHING2_SIGNALING_EVENT_Dead);
+		signal_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_DEAD, error_code);
+		signal_sig2_callback(si->room_id, si->member_id, SCE_NP_MATCHING2_SIGNALING_EVENT_Dead, error_code);
 		retire_all_packets(si);
 	}
 }
@@ -536,15 +555,15 @@ void signaling_handler::update_ext_si_status(std::shared_ptr<signaling_info>& si
 		si->op_activated = true;
 
 		if (si->conn_status != SCE_NP_SIGNALING_CONN_STATUS_ACTIVE)
-			signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_PEER_ACTIVATED);
+			signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_PEER_ACTIVATED, CELL_OK);
 		else
-			signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_MUTUAL_ACTIVATED);
+			signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_MUTUAL_ACTIVATED, CELL_OK);
 	}
 	else if (!op_activated && si->op_activated)
 	{
 		si->op_activated = false;
 
-		signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_PEER_DEACTIVATED);
+		signal_ext_sig_callback(si->conn_id, SCE_NP_SIGNALING_EVENT_EXT_PEER_DEACTIVATED, CELL_OK);
 	}
 }
 
@@ -620,21 +639,35 @@ void signaling_handler::start_sig(u32 conn_id, u32 addr, u16 port)
 	ensure(sig_peers.contains(conn_id));
 	std::shared_ptr<signaling_info> si = ::at32(sig_peers, conn_id);
 
-	si->addr = addr;
-	si->port = port;
+	const auto now = steady_clock::now();
+
+	si->time_last_msg_recvd = now;
+
+	// Only update if those haven't been set before(possible we received a signal_info before)
+	if (si->addr == 0 || si->port == 0)
+	{
+		si->addr = addr;
+		si->port = port;
+	}
 
 	send_signaling_packet(sent_packet, si->addr, si->port);
-	queue_signaling_packet(sent_packet, si, steady_clock::now() + REPEAT_CONNECT_DELAY);
+	queue_signaling_packet(sent_packet, si, now + REPEAT_CONNECT_DELAY);
 	wake_up();
 }
 
-void signaling_handler::stop_sig_nl(u32 conn_id)
+void signaling_handler::stop_sig_nl(u32 conn_id, bool forceful)
 {
 	if (!sig_peers.contains(conn_id))
 		return;
 
 	std::shared_ptr<signaling_info> si = ::at32(sig_peers, conn_id);
 
+	retire_all_packets(si);
+
+	// If forceful we don't go through any transition and don't call any CB
+	if (forceful)
+		si->conn_status = SCE_NP_SIGNALING_CONN_STATUS_INACTIVE;
+
 	// Do not queue packets for an already dead connection
 	if (si->conn_status == SCE_NP_SIGNALING_CONN_STATUS_INACTIVE)
 		return;
@@ -644,13 +677,13 @@ void signaling_handler::stop_sig_nl(u32 conn_id)
 
 	send_signaling_packet(sent_packet, si->addr, si->port);
 	queue_signaling_packet(sent_packet, std::move(si), steady_clock::now() + REPEAT_FINISHED_DELAY);
+	wake_up();
 }
 
-void signaling_handler::stop_sig(u32 conn_id)
+void signaling_handler::stop_sig(u32 conn_id, bool forceful)
 {
 	std::lock_guard lock(data_mutex);
-
-	stop_sig_nl(conn_id);
+	stop_sig_nl(conn_id, forceful);
 }
 
 void signaling_handler::disconnect_sig2_users(u64 room_id)
@@ -661,11 +694,29 @@ void signaling_handler::disconnect_sig2_users(u64 room_id)
 	{
 		if (si->room_id == room_id)
 		{
-			stop_sig_nl(conn_id);
+			stop_sig_nl(conn_id, false);
 		}
 	}
 }
 
+void signaling_handler::send_information_packets(u32 addr, u16 port, const SceNpId& npid)
+{
+	std::lock_guard lock(data_mutex);
+
+	const u32 conn_id = get_always_conn_id(npid);
+	std::shared_ptr<signaling_info> si = ::at32(sig_peers, conn_id);
+	si->addr = addr;
+	si->port = port;
+	si->info_counter = 10;
+
+	auto& sent_packet = sig_packet;
+	sent_packet.command = signal_info;
+
+	send_signaling_packet(sent_packet, addr, port);
+	queue_signaling_packet(sent_packet, si, steady_clock::now() + REPEAT_INFO_DELAY);
+	wake_up();
+}
+
 u32 signaling_handler::get_always_conn_id(const SceNpId& npid)
 {
 	std::string npid_str(reinterpret_cast<const char*>(npid.handle.data));
@@ -709,7 +760,12 @@ u32 signaling_handler::init_sig2(const SceNpId& npid, u64 room_id, u16 member_id
 	auto& si = ::at32(sig_peers, conn_id);
 	si->room_id = room_id;
 	si->member_id = member_id;
-	si->conn_status = SCE_NP_SIGNALING_CONN_STATUS_PENDING;
+
+	// If connection exists from prior state notify
+	if (si->conn_status == SCE_NP_SIGNALING_CONN_STATUS_ACTIVE)
+		signal_sig2_callback(si->room_id, si->member_id, SCE_NP_MATCHING2_SIGNALING_EVENT_Established, CELL_OK);
+	else
+		si->conn_status = SCE_NP_SIGNALING_CONN_STATUS_PENDING;
 
 	return conn_id;
 }
diff --git a/rpcs3/Emu/NP/signaling_handler.h b/rpcs3/Emu/NP/signaling_handler.h
index 6b55af8bc5..fb90fdb8d7 100644
--- a/rpcs3/Emu/NP/signaling_handler.h
+++ b/rpcs3/Emu/NP/signaling_handler.h
@@ -26,6 +26,7 @@ struct signaling_info
 	// Signaling
 	u32 conn_id = 0;
 	bool op_activated = false;
+	u32 info_counter = 10;
 
 	// Matching2
 	u64 room_id = 0;
@@ -48,6 +49,7 @@ enum SignalingCommand : u32
 	signal_confirm,
 	signal_finished,
 	signal_finished_ack,
+	signal_info,
 };
 
 class signaling_handler
@@ -71,7 +73,8 @@ public:
 	void set_sig2_cb(u16 sig2_cb_ctx, vm::ptr<SceNpMatching2SignalingCallback> sig2_cb, vm::ptr<void> sig2_cb_arg);
 
 	void start_sig(u32 conn_id, u32 addr, u16 port);
-	void stop_sig(u32 conn_id);
+	void stop_sig(u32 conn_id, bool forceful);
+	void send_information_packets(u32 addr, u16 port, const SceNpId& npid);
 
 	void disconnect_sig2_users(u64 room_id);
 
@@ -81,6 +84,7 @@ private:
 	static constexpr auto REPEAT_CONNECT_DELAY = std::chrono::milliseconds(200);
 	static constexpr auto REPEAT_PING_DELAY = std::chrono::milliseconds(500);
 	static constexpr auto REPEAT_FINISHED_DELAY = std::chrono::milliseconds(500);
+	static constexpr auto REPEAT_INFO_DELAY = std::chrono::milliseconds(200);
 	static constexpr be_t<u32> SIGNALING_SIGNATURE = (static_cast<u32>('S') << 24 | static_cast<u32>('I') << 16 | static_cast<u32>('G') << 8 | static_cast<u32>('N'));
 	static constexpr le_t<u32> SIGNALING_VERSION = 3;
 
@@ -98,7 +102,7 @@ private:
 
 	struct queued_packet
 	{
-		signaling_packet packet;
+		signaling_packet packet{};
 		std::shared_ptr<signaling_info> sig_info;
 	};
 
@@ -119,17 +123,17 @@ private:
 	u32 get_always_conn_id(const SceNpId& npid);
 	static void update_si_addr(std::shared_ptr<signaling_info>& si, u32 new_addr, u16 new_port);
 	static void update_si_mapped_addr(std::shared_ptr<signaling_info>& si, u32 new_addr, u16 new_port);
-	void update_si_status(std::shared_ptr<signaling_info>& si, s32 new_status);
+	void update_si_status(std::shared_ptr<signaling_info>& si, s32 new_status, int error_code);
 	void update_ext_si_status(std::shared_ptr<signaling_info>& si, bool op_activated);
-	void signal_sig_callback(u32 conn_id, int event);
-	void signal_ext_sig_callback(u32 conn_id, int event) const;
-	void signal_sig2_callback(u64 room_id, u16 member_id, SceNpMatching2Event event) const;
+	void signal_sig_callback(u32 conn_id, int event, int error_code);
+	void signal_ext_sig_callback(u32 conn_id, int event, int error_code) const;
+	void signal_sig2_callback(u64 room_id, u16 member_id, SceNpMatching2Event event, int error_code) const;
 
 	static bool validate_signaling_packet(const signaling_packet* sp);
 	void reschedule_packet(std::shared_ptr<signaling_info>& si, SignalingCommand cmd, steady_clock::time_point new_timepoint);
 	void retire_packet(std::shared_ptr<signaling_info>& si, SignalingCommand cmd);
 	void retire_all_packets(std::shared_ptr<signaling_info>& si);
-	void stop_sig_nl(u32 conn_id);
+	void stop_sig_nl(u32 conn_id, bool forceful);
 
 	shared_mutex data_mutex;
 	atomic_t<u32> wakey = 0;