Merge pull request #22855 from hrydgard/adhoc-transport

Adhoc: Put the relay and P2P transports behind an interface, in their own files
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-10-30 21:19:14 +01:00
commit 6b9830a030
13 files changed
+2551 -2066

No files matched your search

+3
View File
@@ -501,6 +501,9 @@ add_library(Core STATIC
HLE/sceNetApctl.h
HLE/sceNetResolver.cpp
HLE/sceNetResolver.h
HLE/AdhocTransport.h
HLE/AdhocTransportP2P.cpp
HLE/AdhocTransportRelay.cpp
HLE/proAdhoc.h
HLE/proAdhoc.cpp
HLE/proAdhocServer.h
+3
View File
@@ -650,6 +650,8 @@
<ClCompile Include="HLE\sceNet.cpp" />
<ClCompile Include="HLE\sceNet_lib.cpp" />
<ClCompile Include="HLE\NetAdhocCommon.cpp" />
<ClCompile Include="HLE\AdhocTransportP2P.cpp" />
<ClCompile Include="HLE\AdhocTransportRelay.cpp" />
<ClCompile Include="HLE\sceNetAdhoc.cpp" />
<ClCompile Include="HLE\sceNetAdhocMatching.cpp" />
<ClCompile Include="HLE\sceNp.cpp" />
@@ -949,6 +951,7 @@
<ClInclude Include="HLE\HLEHelperThread.h" />
<ClInclude Include="HLE\HLETables.h" />
<ClInclude Include="HLE\KernelWaitHelpers.h" />
<ClInclude Include="HLE\AdhocTransport.h" />
<ClInclude Include="HLE\proAdhoc.h" />
<ClInclude Include="HLE\proAdhocServer.h" />
<ClInclude Include="HLE\ReplaceTables.h" />
+9
View File
@@ -529,6 +529,12 @@
<ClCompile Include="HLE\NetAdhocCommon.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="HLE\AdhocTransportP2P.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="HLE\AdhocTransportRelay.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="HLE\sceNetAdhoc.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
@@ -1902,6 +1908,9 @@
<ClInclude Include="Debugger\DisassemblyManager.h">
<Filter>Debugger</Filter>
</ClInclude>
<ClInclude Include="HLE\AdhocTransport.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="HLE\proAdhoc.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
+218
View File
@@ -0,0 +1,218 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Core/HLE/proAdhoc.h"
#include <chrono>
#include "Core/HLE/sceNetAdhoc.h"
// What a transport found when polling a socket.
struct AdhocPollState {
bool readable = false;
bool writable = false;
bool error = false;
// An alert raised on the socket earlier (P2P reports it in poll, the relay doesn't).
bool alerted = false;
};
// How ad hoc sockets reach the other consoles: through the aemu_postoffice relay, or directly over the
// host's own UDP/TCP sockets (P2P). The sceNetAdhoc functions do everything a PSP does the same either
// way, and call one of these for the parts that depend on the transport. Transport() picks the one in
// use, which follows serverHasRelay.
//
// Ids are PSP socket IDs (1-based slots in adhocSockets). Unless said otherwise, the arguments, the
// socket and its alerts have been checked.
class AdhocTransport {
public:
virtual ~AdhocTransport() {}
// Frees the host side of a socket (not the AdhocSocket), when all are deleted at once.
virtual void CloseHostSocket(AdhocSocket *sock) = 0;
// Whether the connection to a local ad hoc server should be bound to our own local IP, so that
// instances on one machine look like different consoles to it.
virtual bool BindServerConnectionToLocalIp() = 0;
// PDP
// Opens the host side of a PDP socket and registers it in adhocSockets. Returns the socket ID, or an
// error.
virtual int PdpCreate(const SceNetEtherAddr *saddr, int port, int bufferSize, u32 flag, bool isClient, bool enetReliable) = 0;
// Closes it and frees its slot.
virtual int PdpDelete(int id) = 0;
// Sends one datagram to one peer, whose address resolveMAC() has filled into target. Returns the
// number of bytes sent, or SOCKET_ERROR with an errno value in *error.
virtual int PdpSendTo(int id, const SceNetEtherAddr *mac, uint16_t dport, const sockaddr_in &target, const void *data, int len, int *error) = 0;
// The rest of sceNetAdhocPdpRecv.
virtual int PdpRecv(int id, SceNetEtherAddr *saddr, uint16_t *sport, void *buf, int *len, u32 timeout, int flag) = 0;
// The rest of a blocking receive's retry.
virtual int DoBlockingPdpRecv(AdhocSocketRequest &req, s64 &result) = 0;
// Updates rcv_sb_cc, the bytes waiting to be received, for sceNetAdhocGetPdpStat.
virtual void PdpUpdateRecvStat(int id, AdhocSocket *sock) = 0;
// Whether game mode should try sending to / receiving from its socket now.
virtual bool PdpReadyToSend(AdhocSocket *sock) = 0;
virtual bool PdpReadyToRecv(AdhocSocket *sock) = 0;
// PTP
// The host side of sceNetAdhocPtpOpen and sceNetAdhocPtpListen once the ports have been checked.
virtual int PtpOpen(const SceNetEtherAddr *saddr, int sport, const SceNetEtherAddr *daddr, int dport, int bufsize, int rexmt_int, int rexmt_cnt, int flag) = 0;
virtual int PtpListen(const SceNetEtherAddr *saddr, int sport, int bufsize, int rexmt_int, int rexmt_cnt, int backlog, bool isClient, int flag) = 0;
// Closes it and frees its slot.
virtual int PtpClose(int id) = 0;
// The rest of sceNetAdhocPtpSend / PtpRecv / PtpAccept, and their blocking retries.
virtual int PtpSend(int id, const char *data, int *len, int timeout, int flag) = 0;
virtual int PtpRecv(int id, void *buf, int *len, int timeout, int flag) = 0;
virtual int PtpAccept(int id, SceNetEtherAddr *addr, uint16_t *port, int timeout, int flag) = 0;
virtual int DoBlockingPtpSend(AdhocSocketRequest &req, s64 &result) = 0;
virtual int DoBlockingPtpRecv(AdhocSocketRequest &req, s64 &result) = 0;
virtual int DoBlockingPtpAccept(AdhocSocketRequest &req, s64 &result) = 0;
// Starts connecting to sin. Returns 0 if connected, or SOCKET_ERROR with an errno value in *error.
virtual int PtpConnect(int id, const sockaddr_in &sin, int *error) = 0;
// Checks on a connect in progress: > 0 when it can be verified, or SOCKET_ERROR / 0 with *error.
virtual int PtpConnectPoll(AdhocSocket *sock, int *error) = 0;
// Confirms a connection PtpConnect or PtpConnectPoll reported (ret), filling in the peer's address.
virtual int PtpConnectVerify(int id, sockaddr_in *sin, int ret, int *error) = 0;
// Whether a connect that failed with error is still on its way.
virtual bool PtpConnectPending(int error) = 0;
// Whether the connection a socket is making is up now.
virtual bool PtpConnectionUp(AdhocSocket *sock) = 0;
// Whether the connection PtpOpen started has come up (checked before connecting again).
virtual bool PtpConnectedSinceOpen(AdhocSocket *sock) = 0;
// Updates rcv_sb_cc, the bytes waiting to be received, for sceNetAdhocGetPtpStat.
virtual void PtpUpdateRecvStat(AdhocSocket *sock) = 0;
// Sends what's buffered. Returns 0 or an errno value.
virtual int PtpFlush(int socketId) = 0;
// Polling
// Fills in states[i] for each valid socket in sds, waiting up to timeout (microseconds) where the
// transport can wait. Returns a negative value on failure.
virtual int PollWait(SceNetAdhocPollSd *sds, int count, int timeout, AdhocPollState *states) = 0;
// For a PTP socket still connecting: notices the connection coming up, or being refused.
virtual void PollCheckConnecting(int id, AdhocSocket *sock, const AdhocPollState &state) = 0;
// For a readable connected PTP socket: whether that's because the peer closed it.
virtual bool PtpPeerClosed(AdhocSocket *sock) = 0;
};
// Shared with the transports, from sceNetAdhoc.cpp.
#define AEMU_POSTOFFICE_PORT 27313
#define AEMU_POSTOFFICE_ID_BASE 2048
#define RELAY_FAILURE_DISABLE_THRESHOLD_SEC 15
extern std::chrono::time_point<std::chrono::steady_clock> relayLastFailure;
extern bool trackingRelayFailure;
extern bool relayDisabled;
extern bool relayFirstConnect;
uint16_t offset_port_simple(uint16_t port);
uint16_t reverse_port_simple(uint16_t port);
bool offAdhocNetwork();
bool isLocalMac(const SceNetEtherAddr *mac);
void ptpSetEstablished(AdhocSocket *sock);
bool ptpPeerGone(const AdhocSocket *sock);
void ptpNoteSend(AdhocSocket *sock);
// The next free slot in adhocSockets, or MAX_SOCKET.
int FindFreeAdhocSocketSlot();
uint16_t get_unused_pdp_port();
uint16_t get_random_unused_ptp_port();
int WaitBlockingAdhocSocket(u64 threadSocketId, int type, int pspSocketId, void* buffer, s32_le* len, u32 timeoutUS, SceNetEtherAddr* remoteMAC, u16_le* remotePort, const char* reason);
int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEtherAddr* addr, u16_le* port);
int RecreatePtpSocket(int ptpId);
int NetAdhocPtp_Connect(int id, int timeout, int flag, bool allowForcedConnect = true);
// In AdhocTransportRelay.cpp and AdhocTransportP2P.cpp.
class RelayTransport : public AdhocTransport {
public:
void CloseHostSocket(AdhocSocket *sock) override;
int PdpCreate(const SceNetEtherAddr *saddr, int port, int bufferSize, u32 flag, bool isClient, bool enetReliable) override;
int PdpDelete(int id) override;
int PdpSendTo(int id, const SceNetEtherAddr *mac, uint16_t dport, const sockaddr_in &target, const void *data, int len, int *error) override;
int PdpRecv(int id, SceNetEtherAddr *saddr, uint16_t *sport, void *buf, int *len, u32 timeout, int flag) override;
int DoBlockingPdpRecv(AdhocSocketRequest &req, s64 &result) override;
void PdpUpdateRecvStat(int id, AdhocSocket *sock) override;
int PtpOpen(const SceNetEtherAddr *saddr, int sport, const SceNetEtherAddr *daddr, int dport, int bufsize, int rexmt_int, int rexmt_cnt, int flag) override;
int PtpListen(const SceNetEtherAddr *saddr, int sport, int bufsize, int rexmt_int, int rexmt_cnt, int backlog, bool isClient, int flag) override;
int PtpClose(int id) override;
int PtpSend(int id, const char *data, int *len, int timeout, int flag) override;
int PtpRecv(int id, void *buf, int *len, int timeout, int flag) override;
int PtpAccept(int id, SceNetEtherAddr *addr, uint16_t *port, int timeout, int flag) override;
int DoBlockingPtpSend(AdhocSocketRequest &req, s64 &result) override;
int DoBlockingPtpRecv(AdhocSocketRequest &req, s64 &result) override;
int DoBlockingPtpAccept(AdhocSocketRequest &req, s64 &result) override;
int PtpConnect(int id, const sockaddr_in &sin, int *error) override;
int PtpConnectPoll(AdhocSocket *sock, int *error) override;
bool PtpConnectionUp(AdhocSocket *sock) override;
void PtpUpdateRecvStat(AdhocSocket *sock) override;
int PtpFlush(int socketId) override;
int PollWait(SceNetAdhocPollSd *sds, int count, int timeout, AdhocPollState *states) override;
bool BindServerConnectionToLocalIp() override { return false; }
bool PdpReadyToSend(AdhocSocket *sock) override { return true; }
bool PdpReadyToRecv(AdhocSocket *sock) override { return true; }
// A connection's handle only comes once it's up, so there's no in-between to check.
int PtpConnectVerify(int id, sockaddr_in *sin, int ret, int *error) override { return ret; }
bool PtpConnectPending(int error) override { return true; }
bool PtpConnectedSinceOpen(AdhocSocket *sock) override { return false; }
void PollCheckConnecting(int id, AdhocSocket *sock, const AdhocPollState &state) override {}
bool PtpPeerClosed(AdhocSocket *sock) override { return false; }
};
class P2PTransport : public AdhocTransport {
public:
void CloseHostSocket(AdhocSocket *sock) override;
int PdpCreate(const SceNetEtherAddr *saddr, int port, int bufferSize, u32 flag, bool isClient, bool enetReliable) override;
int PdpDelete(int id) override;
int PdpSendTo(int id, const SceNetEtherAddr *mac, uint16_t dport, const sockaddr_in &target, const void *data, int len, int *error) override;
int PdpRecv(int id, SceNetEtherAddr *saddr, uint16_t *sport, void *buf, int *len, u32 timeout, int flag) override;
int DoBlockingPdpRecv(AdhocSocketRequest &req, s64 &result) override;
void PdpUpdateRecvStat(int id, AdhocSocket *sock) override;
int PtpOpen(const SceNetEtherAddr *saddr, int sport, const SceNetEtherAddr *daddr, int dport, int bufsize, int rexmt_int, int rexmt_cnt, int flag) override;
int PtpListen(const SceNetEtherAddr *saddr, int sport, int bufsize, int rexmt_int, int rexmt_cnt, int backlog, bool isClient, int flag) override;
int PtpClose(int id) override;
int PtpSend(int id, const char *data, int *len, int timeout, int flag) override;
int PtpRecv(int id, void *buf, int *len, int timeout, int flag) override;
int PtpAccept(int id, SceNetEtherAddr *addr, uint16_t *port, int timeout, int flag) override;
int DoBlockingPtpSend(AdhocSocketRequest &req, s64 &result) override;
int DoBlockingPtpRecv(AdhocSocketRequest &req, s64 &result) override;
int DoBlockingPtpAccept(AdhocSocketRequest &req, s64 &result) override;
int PtpConnect(int id, const sockaddr_in &sin, int *error) override;
int PtpConnectPoll(AdhocSocket *sock, int *error) override;
bool PtpConnectionUp(AdhocSocket *sock) override;
void PtpUpdateRecvStat(AdhocSocket *sock) override;
int PtpFlush(int socketId) override;
int PollWait(SceNetAdhocPollSd *sds, int count, int timeout, AdhocPollState *states) override;
bool BindServerConnectionToLocalIp() override { return true; }
bool PdpReadyToSend(AdhocSocket *sock) override;
bool PdpReadyToRecv(AdhocSocket *sock) override;
int PtpConnectVerify(int id, sockaddr_in *sin, int ret, int *error) override;
bool PtpConnectPending(int error) override;
bool PtpConnectedSinceOpen(AdhocSocket *sock) override;
void PollCheckConnecting(int id, AdhocSocket *sock, const AdhocPollState &state) override;
bool PtpPeerClosed(AdhocSocket *sock) override;
};
AdhocTransport &Transport();
File diff suppressed because it is too large. Load diff
+982
View File
@@ -0,0 +1,982 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// The relay transport: ad hoc sockets carried by an aemu_postoffice server.
#include "ppsspp_config.h"
#include <mutex>
#include <string>
#include "Common/Net/SocketCompat.h"
#include "Common/Data/Text/I18n.h"
#include "Common/System/OSD.h"
#include "Common/StringUtils.h"
#include "Common/TimeUtil.h"
#include "Core/Config.h"
#include "Core/CoreTiming.h"
#include "Core/Util/PortManager.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/ErrorCodes.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/HLE/sceNetAdhoc.h"
#include "Core/HLE/AdhocTransport.h"
#include "Core/HLE/NetAdhocCommon.h"
#include "Core/HLE/NetInetConstants.h"
#include "ext/aemu_postoffice/client/postoffice_client.h"
#ifdef _WIN32
#undef errno
#define errno WSAGetLastError()
#endif
void RelayTransport::CloseHostSocket(AdhocSocket *sock) {
if (sock->type == SOCK_PTP) {
// sync
if (sock->connectThread != NULL) {
sock->connectThread->join();
delete sock->connectThread;
}
void *socket = sock->postofficeHandle;
if (socket != NULL) {
if (sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN) {
ptp_listen_close(socket);
} else {
ptp_close(socket);
}
}
} else {
if (sock->postofficeHandle != NULL) {
pdp_delete(sock->postofficeHandle);
}
}
}
static void handle_relay_connect_failure() {
auto n = GetI18NCategory(I18NCat::NETWORKING);
if (!trackingRelayFailure) {
trackingRelayFailure = true;
relayLastFailure = std::chrono::steady_clock::now();
}
int been_failing_since_sec = (std::chrono::steady_clock::now() - relayLastFailure) / std::chrono::seconds(1);
if (been_failing_since_sec < RELAY_FAILURE_DISABLE_THRESHOLD_SEC) {
g_OSD.Show(OSDType::MESSAGE_ERROR, n->T("Failed connecting to relay server"), 4.0f, "relay_status");
} else {
g_OSD.Show(OSDType::MESSAGE_ERROR, ApplySafeSubstitutions(n->T("Failed connecting to relay server for %1 seconds, relay disabled"), RELAY_FAILURE_DISABLE_THRESHOLD_SEC), 10.0f, "relay_status");
relayDisabled = true;
}
}
static void handle_relay_connect_success() {
auto n = GetI18NCategory(I18NCat::NETWORKING);
if (trackingRelayFailure) {
g_OSD.Show(OSDType::MESSAGE_SUCCESS, n->T("Connection to relay has recovered"), 2.0f, "relay_status");
}
if (relayFirstConnect) {
g_OSD.Show(OSDType::MESSAGE_SUCCESS, n->T("Connected to relay"), 2.0f, "relay_status");
relayFirstConnect = false;
}
trackingRelayFailure = false;
}
static void *pdp_postoffice_recover(int idx) {
if (relayDisabled) {
return NULL;
}
AdhocSocket *internal = adhocSockets[idx];
if (internal->postofficeHandle != NULL) {
return internal->postofficeHandle;
}
INFO_LOG(Log::sceNet, "%s: recovering pdp socket id %d", __func__, idx + 1);
struct aemu_postoffice_sock_addr addr;
auto server_ip = GetAdhocServerIP();
addr.addr = server_ip.in.sin_addr.s_addr;
addr.port = htons(AEMU_POSTOFFICE_PORT);
SceNetEtherAddr local_mac;
getLocalMac(&local_mac);
int state;
internal->postofficeHandle = pdp_create_v4(&addr, (const char *)&local_mac, offset_port_simple(internal->data.pdp.lport), netAdhocUseUdp, internal->postofficePdpEnetReliable, NULL, NULL, &state);
if (state != AEMU_POSTOFFICE_CLIENT_OK) {
ERROR_LOG(Log::sceNet, "%s: failed creating pdp socket on aemu postoffice library, %d", __func__, state);
handle_relay_connect_failure();
} else {
handle_relay_connect_success();
}
INFO_LOG(Log::sceNet, "%s: pdp recovery for id %d: %p", __func__, idx + 1, internal->postofficeHandle);
return internal->postofficeHandle;
}
static int pdp_peek_next_size_postoffice(int idx) {
AdhocSocket *internal = adhocSockets[idx];
void *pdp_sock = pdp_postoffice_recover(idx);
if (pdp_sock == NULL) {
return 0;
}
int peek_status = pdp_peek_next_size(pdp_sock);
if (peek_status == AEMU_POSTOFFICE_CLIENT_SESSION_DEAD) {
pdp_delete(internal->postofficeHandle);
internal->postofficeHandle = NULL;
return 0;
}
return peek_status;
}
static int pdp_recv_postoffice(int idx, SceNetEtherAddr *saddr, uint16_t *sport, void *data, int *len) {
AdhocSocket *internal = adhocSockets[idx];
void *pdp_sock = pdp_postoffice_recover(idx);
if (pdp_sock == NULL) {
return SOCKET_ERROR;
}
int sport_copy = 0;
SceNetEtherAddr saddr_copy = {0};
int len_copy = *len;
int pdp_recv_status = pdp_recv(pdp_sock, (char *)&saddr_copy, &sport_copy, (char *)data, &len_copy, true);
if (pdp_recv_status == AEMU_POSTOFFICE_CLIENT_SESSION_DEAD) {
handle_relay_connect_failure();
pdp_delete(internal->postofficeHandle);
internal->postofficeHandle = NULL;
return SOCKET_ERROR;
}
if (pdp_recv_status == AEMU_POSTOFFICE_CLIENT_SESSION_WOULD_BLOCK) {
return SOCKET_ERROR;
}
// Out of the group, nothing from anyone else reaches us (pspautotests net/adhoc/pair/vanish).
if (offAdhocNetwork() && !isLocalMac(&saddr_copy)) {
return SOCKET_ERROR;
}
*len = len_copy;
if (saddr != NULL) {
*saddr = saddr_copy;
}
if (sport != NULL) {
*sport = reverse_port_simple(sport_copy);
}
return 0;
}
int RelayTransport::DoBlockingPdpRecv(AdhocSocketRequest &req, s64 &result) {
int next_size = pdp_peek_next_size_postoffice(req.id - 1);
if (next_size == 0) {
// no next packet
} else if (next_size > *req.length) {
// next is larger than current buffer
result = SCE_NET_ADHOC_ERROR_NOT_ENOUGH_SPACE;
*req.length = next_size;
return 0;
} else if (pdp_recv_postoffice(req.id - 1, req.remoteMAC, req.remotePort, req.buffer, req.length) == 0) {
// we got data into the request
result = 0;
return 0;
}
u64 now = CoreTiming::GetGlobalTimeUs();
if (req.timeout == 0 || now - req.startTime <= req.timeout) {
// Try again later
return -1;
}
result = SCE_NET_ADHOC_ERROR_TIMEOUT;
DEBUG_LOG(Log::sceNet, "sceNetAdhocPdpRecv[%i]: Socket Error (%i)", req.id, EAGAIN);
return 0;
}
int RelayTransport::PdpRecv(int id, SceNetEtherAddr *saddr, uint16_t *sport, void *buf, int *len, u32 timeout, int flag) {
int next_size = pdp_peek_next_size_postoffice(id - 1);
if (next_size == 0) {
// no next packet
} else if (next_size > *len) {
// next is larger than current buffer
*len = next_size;
return SCE_NET_ADHOC_ERROR_NOT_ENOUGH_SPACE;
} else if (pdp_recv_postoffice(id - 1, saddr, sport, buf, len) == 0) {
// we got data
hleEatMicro(50);
return 0;
}
if (flag == 0) {
// Simulate blocking behaviour with non-blocking socket
u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | adhocSockets[id - 1]->data.pdp.id;
return WaitBlockingAdhocSocket(threadSocketId, PDP_RECV, id, buf, len, timeout, saddr, sport, "pdp recv");
}
VERBOSE_LOG(Log::sceNet, "%08x=sceNetAdhocPdpRecv: would block", SCE_NET_ADHOC_ERROR_WOULD_BLOCK); // Temporary fix to avoid a crash on the Logs due to trying to Logs syscall's argument from another thread (ie. AdhocMatchingInput thread)
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
static int pdp_send_postoffice(int idx, const SceNetEtherAddr *daddr, uint16_t dport, const void *data, int len) {
AdhocSocket *internal = adhocSockets[idx];
void *pdp_sock = pdp_postoffice_recover(idx);
if (pdp_sock == NULL) {
return SOCKET_ERROR;
}
// Out of the group, it goes into the air and reaches nobody (pspautotests net/adhoc/pair/vanish).
if (offAdhocNetwork() && !isLocalMac(daddr)) {
return 0;
}
SceNetEtherAddr fixed_daddr = *daddr;
fixGameMac(&fixed_daddr);
int pdp_send_status = pdp_send(pdp_sock, (const char *)&fixed_daddr, offset_port_simple(dport), (char *)data, len, true);
if (pdp_send_status == AEMU_POSTOFFICE_CLIENT_SESSION_DEAD) {
handle_relay_connect_failure();
pdp_delete(internal->postofficeHandle);
internal->postofficeHandle = NULL;
return SOCKET_ERROR;
}
if (pdp_send_status == AEMU_POSTOFFICE_CLIENT_SESSION_WOULD_BLOCK) {
return SOCKET_ERROR;
}
if (pdp_send_status == AEMU_POSTOFFICE_CLIENT_OUT_OF_MEMORY) {
// this is pretty critical
ERROR_LOG(Log::sceNet, "%s: critical: huge client buf %d what is going on please fix", __func__, len);
}
return 0;
}
int RelayTransport::PdpSendTo(int id, const SceNetEtherAddr *mac, uint16_t dport, const sockaddr_in &target, const void *data, int len, int *error) {
if (pdp_send_postoffice(id - 1, mac, dport, data, len) == 0) {
return len;
}
*error = EAGAIN;
return SOCKET_ERROR;
}
static int ptp_send_postoffice(int idx, const void *data, int *len) {
if (relayDisabled) {
return SOCKET_ERROR;
}
AdhocSocket *internal = adhocSockets[idx];
if (internal->postofficeHandle == NULL) {
// this should only happen on ptp_open sockets, where ptp_connect is still in progress on another thread
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
// Out of the group, the send succeeds and the data goes nowhere (pspautotests net/adhoc/pair/vanish).
if (offAdhocNetwork()) {
return 0;
}
if (ptpPeerGone(internal)) {
return SOCKET_ERROR;
}
if (*len > AEMU_POSTOFFICE_PTP_BLOCK_MAX) {
// force fragmentation for giant sends
*len = AEMU_POSTOFFICE_PTP_BLOCK_MAX;
}
int ptp_send_status = ptp_send(internal->postofficeHandle, (const char *)data, *len, true);
ptpNoteSend(internal);
if (ptp_send_status == AEMU_POSTOFFICE_CLIENT_SESSION_DEAD) {
// the session is dead, need to be reflected to the other side
// this is not necessarily a relay failure, could simply be the other side disconnecting
//handle_relay_connect_failure();
return SOCKET_ERROR;
}
if (ptp_send_status == AEMU_POSTOFFICE_CLIENT_SESSION_WOULD_BLOCK) {
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
if (ptp_send_status == AEMU_POSTOFFICE_CLIENT_OUT_OF_MEMORY) {
// this is pretty critical
ERROR_LOG(Log::sceNet, "%s: critical: huge client buf %d what is going on please fix", __func__, len ? *len : 0);
}
return 0;
}
int RelayTransport::DoBlockingPtpSend(AdhocSocketRequest &req, s64 &result) {
auto sock = adhocSockets[req.id - 1];
auto& ptpsocket = sock->data.ptp;
int ret = ptp_send_postoffice(req.id - 1, req.buffer, req.length);
if (ret == 0) {
// sent
// Set to Established on successful Send when an attempt to Connect was initiated
if (ptpsocket.state == ADHOC_PTP_STATE_SYN_SENT)
ptpSetEstablished(sock);
DEBUG_LOG(Log::sceNet, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %s:%u\n", req.id, ptpsocket.lport, ret, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport);
result = 0;
return 0;
}
if (ret == SOCKET_ERROR) {
ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
result = SCE_NET_ADHOC_ERROR_DISCONNECTED;
DEBUG_LOG(Log::sceNet, "sceNetAdhocPtpSend[%i]: Socket Error (%i)", req.id, 0);
return 0;
}
// SCE_NET_ADHOC_ERROR_WOULD_BLOCK
u64 now = CoreTiming::GetGlobalTimeUs();
if (req.timeout == 0 || now - req.startTime <= req.timeout) {
return -1;
}
result = SCE_NET_ADHOC_ERROR_TIMEOUT;
DEBUG_LOG(Log::sceNet, "sceNetAdhocPtpSend[%i]: Socket Error (%i)", req.id, EAGAIN);
return 0;
}
static int ptp_recv_postoffice(int idx, void *data, int *len) {
if (relayDisabled) {
return SOCKET_ERROR;
}
AdhocSocket *internal = adhocSockets[idx];
if (internal->postofficeHandle == NULL) {
// this should only happen on ptp_open sockets, where ptp_connect is still in progress on another thread
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
// Out of the group, nothing arrives, and the connection doesn't fail either (pspautotests
// net/adhoc/pair/vanish).
if (offAdhocNetwork()) {
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
if (ptpPeerGone(internal)) {
return SOCKET_ERROR;
}
int len_copy = *len;
int ptp_recv_status = ptp_recv(internal->postofficeHandle, (char *)data, &len_copy, true);
if (ptp_recv_status == AEMU_POSTOFFICE_CLIENT_SESSION_DEAD) {
// the session is dead, need to be reflected to the other side
// this is not necessarily a relay failure, could simply be the other side disconnecting
//handle_relay_connect_failure();
return SOCKET_ERROR;
}
if (ptp_recv_status == AEMU_POSTOFFICE_CLIENT_SESSION_WOULD_BLOCK) {
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
// AEMU_POSTOFFICE_CLIENT_SESSION_DATA_TRUNC is okay, it just means it has data in it's user space buffer
*len = len_copy;
return 0;
}
int RelayTransport::DoBlockingPtpRecv(AdhocSocketRequest &req, s64 &result) {
auto& ptpsocket = adhocSockets[req.id - 1]->data.ptp;
int ret = ptp_recv_postoffice(req.id - 1, req.buffer, req.length);
if (ret == 0) {
// we got data
result = 0;
return 0;
}
if (ret == SOCKET_ERROR) {
// the socket died, let the game know
ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
result = SCE_NET_ADHOC_ERROR_DISCONNECTED;
return 0;
}
// SCE_NET_ADHOC_ERROR_WOULD_BLOCK
u64 now = CoreTiming::GetGlobalTimeUs();
if (req.timeout == 0 || now - req.startTime <= req.timeout) {
return -1;
}
result = SCE_NET_ADHOC_ERROR_TIMEOUT;
DEBUG_LOG(Log::sceNet, "sceNetAdhocPtpRecv[%i]: Socket Error (%i)", req.id, EAGAIN);
return 0;
}
static void *ptp_listen_postoffice_recover(int idx) {
if (relayDisabled) {
return NULL;
}
AdhocSocket *internal = adhocSockets[idx];
if (internal->postofficeHandle != NULL) {
return internal->postofficeHandle;
}
INFO_LOG(Log::sceNet, "%s: recovering ptp listen socket id %d", __func__, idx + 1);
struct aemu_postoffice_sock_addr addr;
auto server_ip = GetAdhocServerIP();
addr.addr = server_ip.in.sin_addr.s_addr;
addr.port = htons(AEMU_POSTOFFICE_PORT);
int state;
internal->postofficeHandle = ptp_listen_v4(&addr, (const char*)&internal->data.ptp.laddr, internal->data.ptp.lport + portOffset, netAdhocUseUdp, NULL, NULL, &state);
if (state != AEMU_POSTOFFICE_CLIENT_OK) {
ERROR_LOG(Log::sceNet, "%s: failed recovering ptp listen socket, %d", __func__, state);
handle_relay_connect_failure();
} else {
handle_relay_connect_success();
}
INFO_LOG(Log::sceNet, "%s: ptp listen recovery for id %d: %p", __func__, idx + 1, internal->postofficeHandle);
return internal->postofficeHandle;
}
static int ptp_accept_postoffice(int idx, SceNetEtherAddr *saddr, uint16_t *sport) {
void *ptp_listen_socket = ptp_listen_postoffice_recover(idx);
if (ptp_listen_socket == NULL) {
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
int state = 0;
int port_cpy = 0;
SceNetEtherAddr mac_cpy = {0};
void *new_ptp_socket = ptp_accept(ptp_listen_socket, (char *)&mac_cpy, &port_cpy, true, NULL, NULL, &state);
if (new_ptp_socket == NULL) {
if (state == AEMU_POSTOFFICE_CLIENT_SESSION_DEAD) {
handle_relay_connect_failure();
ptp_listen_close(adhocSockets[idx]->postofficeHandle);
adhocSockets[idx]->postofficeHandle = NULL;
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
if (state == AEMU_POSTOFFICE_CLIENT_OUT_OF_MEMORY) {
// pretty critical here
ERROR_LOG(Log::sceNet, "%s: aemu_postoffice ran out of memory to accept a new connection", __func__);
}
// AEMU_POSTOFFICE_CLIENT_SESSION_WOULD_BLOCK
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
// we have a new socket
handle_relay_connect_success();
AdhocSocket *internal = (AdhocSocket *)malloc(sizeof(AdhocSocket));
if (internal == NULL) {
ERROR_LOG(Log::sceNet, "%s: critical: ran out of heap memory while accepting new connection", __func__);
ptp_close(new_ptp_socket);
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
memset(internal, 0, sizeof(AdhocSocket));
internal->retry_interval = adhocSockets[idx]->retry_interval;
internal->retry_count = adhocSockets[idx]->retry_count;
internal->attemptCount = 1; // Connected, so a closed socket reports DISCONNECTED (as the native accept does)
internal->type = SOCK_PTP;
internal->postofficeHandle = new_ptp_socket;
internal->data.ptp.laddr = adhocSockets[idx]->data.ptp.laddr;
internal->data.ptp.lport = adhocSockets[idx]->data.ptp.lport;
internal->data.ptp.paddr = mac_cpy;
internal->data.ptp.pport = reverse_port_simple(port_cpy);
ptpSetEstablished(internal);
internal->data.ptp.rcv_sb_cc = adhocSockets[idx]->data.ptp.rcv_sb_cc;
internal->data.ptp.snd_sb_cc = adhocSockets[idx]->data.ptp.snd_sb_cc;
internal->data.ptp.id = AEMU_POSTOFFICE_ID_BASE + idx;
internal->flags = 0;
internal->connectThread = NULL;
AdhocSocket **slot = NULL;
int i;
i = FindFreeAdhocSocketSlot();
if (i < MAX_SOCKET) {
slot = &adhocSockets[i];
}
if (slot == NULL) {
ptp_close(new_ptp_socket);
free(internal);
ERROR_LOG(Log::sceNet, "%s: critical: cannot find an empty mapper slot", __func__);
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
*slot = internal;
if (saddr != NULL) {
*saddr = mac_cpy;
}
if (sport != NULL) {
*sport = reverse_port_simple(port_cpy);
}
INFO_LOG(Log::sceNet, "%s: accepted ptp socket with id %d %p", __func__, i + 1, internal->postofficeHandle);
return i + 1;
}
int RelayTransport::DoBlockingPtpAccept(AdhocSocketRequest &req, s64 &result) {
int ret = ptp_accept_postoffice(req.id - 1, req.remoteMAC, req.remotePort);
if (ret >= 0) {
result = ret;
return 0;
}
// SCE_NET_ADHOC_ERROR_WOULD_BLOCK
u64 now = CoreTiming::GetGlobalTimeUs();
if (req.timeout == 0 || now - req.startTime <= req.timeout) {
return -1;
}
result = SCE_NET_ADHOC_ERROR_TIMEOUT;
DEBUG_LOG(Log::sceNet, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", req.id, EAGAIN);
return 0;
}
static int ptp_connect_postoffice(int idx, const char *caller) {
if (relayDisabled) {
return SCE_NET_ADHOC_ERROR_CONNECTION_REFUSED;
}
AdhocSocket *internal = adhocSockets[idx];
struct aemu_postoffice_sock_addr addr;
auto server_ip = GetAdhocServerIP();
addr.addr = server_ip.in.sin_addr.s_addr;
addr.port = htons(AEMU_POSTOFFICE_PORT);
if (internal->postofficeHandle != NULL) {
return 0;
}
if (internal->connectThreadDone) {
if (internal->connectThread != NULL) {
internal->connectThread->join();
delete internal->connectThread;
internal->connectThread = NULL;
}
internal->connectThreadDone = false;
internal->connectThreadResult = 0;
internal->connectThread = new std::thread([internal, addr, idx] {
int state;
SceNetEtherAddr fixed_daddr = internal->data.ptp.paddr;
fixGameMac(&fixed_daddr);
void *ptp_socket = ptp_connect_v4(&addr, (const char *)&internal->data.ptp.laddr, offset_port_simple(internal->data.ptp.lport), (const char *)&fixed_daddr, offset_port_simple(internal->data.ptp.pport), netAdhocUseUdp, NULL, NULL, &state);
if (ptp_socket == NULL) {
internal->connectThreadResult = SCE_NET_ADHOC_ERROR_CONNECTION_REFUSED;
ERROR_LOG(Log::sceNet, "%s: failed connecting to ptp socket, %d", __func__, state);
// do not count ptp connect failure as relay failure, since it could be the other client not accepting the connection
//handle_relay_connect_failure();
internal->connectThreadDone = true;
return;
}
// see above, ptp connect result is not used for checking if relay is working
//handle_relay_connect_success();
internal->postofficeHandle = ptp_socket;
internal->connectThreadResult = 0;
INFO_LOG(Log::sceNet, "%s: connected ptp socket with id %d %p", __func__, idx + 1, internal->postofficeHandle);
internal->connectThreadDone = true;
return;
});;
DEBUG_LOG(Log::sceNet, "%s: started connect thread on id %d", __func__, idx + 1);
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
DEBUG_LOG(Log::sceNet, "%s: id %d is connecting", __func__, idx + 1);
return SCE_NET_ADHOC_ERROR_WOULD_BLOCK;
}
bool RelayTransport::PtpConnectionUp(AdhocSocket *sock) {
return sock->postofficeHandle != NULL;
}
int RelayTransport::PtpConnect(int id, const sockaddr_in &sin, int *error) {
if (ptp_connect_postoffice(id - 1, __func__) != 0) {
*error = EAGAIN;
return SOCKET_ERROR;
}
return 0;
}
int RelayTransport::PtpConnectPoll(AdhocSocket *sock, int *error) {
if (sock->postofficeHandle != NULL) {
// the handle is there, we're ready to go
*error = 0;
return 1;
}
if (sock->connectThreadDone && sock->connectThreadResult == SCE_NET_ADHOC_ERROR_CONNECTION_REFUSED) {
*error = ECONNREFUSED;
} else {
*error = EAGAIN;
}
return SOCKET_ERROR;
}
static int pdp_create_postoffice(const SceNetEtherAddr *saddr, int sport, int bufsize, bool enet_reliable) {
if (relayDisabled) {
return hleLogDebug(Log::sceNet, ERROR_NET_NO_SPACE, "relay disabled");
}
AdhocSocket *internal = (AdhocSocket*)malloc(sizeof(AdhocSocket));
if (internal == NULL) {
return hleLogDebug(Log::sceNet, ERROR_NET_NO_SPACE, "net no space");
}
memset(internal, 0, sizeof(AdhocSocket));
internal->type = SOCK_PDP;
internal->postofficeHandle = NULL;
internal->data.pdp.laddr = *saddr;
internal->data.pdp.lport = sport;
internal->data.pdp.rcv_sb_cc = bufsize;
internal->flags = 0;
internal->lastAttempt = 0;
internal->internalLastAttempt = 0;
internal->postofficePdpEnetReliable = enet_reliable;
AdhocSocket **free_slot = NULL;
int i;
i = FindFreeAdhocSocketSlot();
if (i < MAX_SOCKET) {
free_slot = &adhocSockets[i];
}
if (free_slot == NULL) {
free(internal);
return ERROR_NET_NO_SPACE;
}
internal->data.pdp.id = AEMU_POSTOFFICE_ID_BASE + i;
*free_slot = internal;
pdp_postoffice_recover(i);
INFO_LOG(Log::sceNet, "%s: created pdp socket with id %d %p", __func__, i + 1, internal->postofficeHandle);
return i + 1;
}
int RelayTransport::PdpCreate(const SceNetEtherAddr *saddr, int port, int bufferSize, u32 flag, bool isClient, bool enetReliable) {
return pdp_create_postoffice(saddr, port, bufferSize, enetReliable);
}
static bool postoffice_can_recv_or_listen_has_request(int idx) {
AdhocSocket *internal = adhocSockets[idx];
if (internal->type == SOCK_PTP) {
if (internal->data.ptp.state == ADHOC_PTP_STATE_LISTEN) {
void *ptp_listen_handle = ptp_listen_postoffice_recover(idx);
if (ptp_listen_handle == NULL) {
return false;
}
int has_request = ptp_listen_has_request(ptp_listen_handle);
if (has_request == AEMU_POSTOFFICE_CLIENT_SESSION_DEAD) {
internal->postofficeHandle = NULL;
ptp_listen_close(ptp_listen_handle);
return false;
}
if (has_request) {
return true;
}
} else if (internal->data.ptp.state == ADHOC_PTP_STATE_ESTABLISHED) {
void *ptp_handle = internal->postofficeHandle;
if (ptp_handle == NULL) {
return false;
}
int peek_len = ptp_peek_next_size(ptp_handle);
if (peek_len == AEMU_POSTOFFICE_CLIENT_SESSION_DEAD) {
return false;
}
if (peek_len > 0) {
return true;
}
}
} else {
void *pdp_handle = pdp_postoffice_recover(idx);
if (pdp_handle == NULL) {
return false;
}
int peek_len = pdp_buffered_data_size(pdp_handle);
if (peek_len == AEMU_POSTOFFICE_CLIENT_SESSION_DEAD) {
internal->postofficeHandle = NULL;
pdp_delete(pdp_handle);
return false;
}
if (peek_len > 0) {
return true;
}
}
return false;
}
static bool postoffice_can_send(int idx) {
AdhocSocket *internal = adhocSockets[idx];
if (internal->type == SOCK_PTP && internal->data.ptp.state == ADHOC_PTP_STATE_ESTABLISHED) {
void *ptp_handle = internal->postofficeHandle;
if (ptp_handle == NULL) {
return false;
}
if (ptp_is_dead(ptp_handle)) {
return false;
}
return ptp_send_buf_not_full(ptp_handle);
} else {
void *pdp_handle = pdp_postoffice_recover(idx);
if (pdp_handle == NULL) {
return false;
}
if (pdp_is_dead(pdp_handle)) {
internal->postofficeHandle = NULL;
pdp_delete(pdp_handle);
return false;
}
return pdp_send_buf_not_full(pdp_handle);
}
return false;
}
int RelayTransport::PollWait(SceNetAdhocPollSd *sds, int count, int timeout, AdhocPollState *states) {
for (int i = 0; i < count; i++) {
if (sds[i].id > 0 && sds[i].id <= MAX_SOCKET && adhocSockets[sds[i].id - 1] != NULL) {
states[i].readable = postoffice_can_recv_or_listen_has_request(sds[i].id - 1);
states[i].writable = (sds[i].events & ADHOC_EV_SEND) && postoffice_can_send(sds[i].id - 1);
}
}
return 0;
}
static int pdp_delete_postoffice(int idx) {
AdhocSocket *internal = adhocSockets[idx];
if (internal->postofficeHandle != NULL) {
pdp_delete(internal->postofficeHandle);
}
adhocSockets[idx] = NULL;
free(internal);
INFO_LOG(Log::sceNet, "%s: closed pdp socket with id %d", __func__, idx + 1);
return 0;
}
int RelayTransport::PdpDelete(int id) {
return pdp_delete_postoffice(id - 1);
}
void RelayTransport::PdpUpdateRecvStat(int id, AdhocSocket *sock) {
void *postofficeHandle = pdp_postoffice_recover(id - 1);
if (postofficeHandle != NULL) {
sock->data.pdp.rcv_sb_cc = pdp_buffered_data_size(postofficeHandle);
}
}
void RelayTransport::PtpUpdateRecvStat(AdhocSocket *sock) {
if (sock->postofficeHandle != NULL) {
sock->data.ptp.rcv_sb_cc = ptp_peek_next_size(sock->postofficeHandle);
} else {
sock->data.ptp.rcv_sb_cc = 0;
}
}
static int ptp_open_postoffice(const SceNetEtherAddr *saddr, uint16_t sport, const SceNetEtherAddr *daddr, uint16_t dport, uint32_t bufsize, uint32_t rexmt_int, int rexmt_cnt) {
AdhocSocket *internal = (AdhocSocket *)malloc(sizeof(AdhocSocket));
if (internal == NULL) {
ERROR_LOG(Log::sceNet, "%s: ran out of heap memory trying to open ptp socket", __func__);
return ERROR_NET_NO_SPACE;
}
memset(internal, 0, sizeof(AdhocSocket));
internal->retry_interval = rexmt_int;
internal->retry_count = rexmt_cnt;
internal->type = SOCK_PTP;
internal->postofficeHandle = NULL;
internal->data.ptp.state = ADHOC_PTP_STATE_CLOSED;
internal->data.ptp.laddr = *saddr;
internal->data.ptp.lport = sport == 0 ? get_random_unused_ptp_port() : sport;
internal->data.ptp.paddr = *daddr;
internal->data.ptp.pport = dport;
internal->data.ptp.rcv_sb_cc = bufsize;
internal->data.ptp.snd_sb_cc = 0;
internal->flags = 0;
internal->connectThread = NULL;
internal->connectThreadDone = true;
internal->lastAttempt = 0;
internal->internalLastAttempt = 0;
AdhocSocket **slot = NULL;
int i;
i = FindFreeAdhocSocketSlot();
if (i < MAX_SOCKET) {
slot = &adhocSockets[i];
}
if (slot == NULL) {
free(internal);
ERROR_LOG(Log::sceNet, "%s: cannot find free socket mapper slot while opening ptp socket", __func__);
return ERROR_NET_NO_SPACE;
}
internal->data.ptp.id = AEMU_POSTOFFICE_ID_BASE + i;
*slot = internal;
INFO_LOG(Log::sceNet, "%s: created ptp socket with id %d", __func__, i + 1);
// PtpOpen starts connecting by itself: on the PSP the client is ESTABLISHED right after it (pspautotests
// net/adhoc/ptp). The Warriors sends without calling PtpConnect.
NetAdhocPtp_Connect(i + 1, 0, 1, false);
return i + 1;
}
int RelayTransport::PtpOpen(const SceNetEtherAddr *saddr, int sport, const SceNetEtherAddr *daddr, int dport, int bufsize, int rexmt_int, int rexmt_cnt, int flag) {
return ptp_open_postoffice(saddr, sport, daddr, dport, bufsize, rexmt_int, rexmt_cnt);
}
int RelayTransport::PtpAccept(int id, SceNetEtherAddr *addr, uint16_t *port, int timeout, int flag) {
int newsocket = ptp_accept_postoffice(id - 1, addr, port);
if (newsocket >= 0) {
return newsocket;
}
// SCE_NET_ADHOC_ERROR_WOULD_BLOCK
if (flag == 0) {
// Simulate blocking behaviour with non-blocking socket
u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | adhocSockets[id - 1]->data.ptp.id;
return WaitBlockingAdhocSocket(threadSocketId, PTP_ACCEPT, id, nullptr, nullptr, timeout, addr, port, "ptp accept");
}
// Prevent spamming Debug Log with retries of non-bocking socket
VERBOSE_LOG(Log::sceNet, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", id, EAGAIN);
return hleLogVerbose(Log::sceNet, SCE_NET_ADHOC_ERROR_WOULD_BLOCK, "would block");
}
static int ptp_close_postoffice(int idx) {
AdhocSocket *internal = adhocSockets[idx];
// sync
if (internal->connectThread != NULL) {
internal->connectThread->join();
delete internal->connectThread;
}
void *socket = internal->postofficeHandle;
if (socket != NULL) {
if (internal->data.ptp.state == ADHOC_PTP_STATE_LISTEN) {
ptp_listen_close(socket);
} else {
ptp_close(socket);
}
}
adhocSockets[idx] = NULL;
free(internal);
INFO_LOG(Log::sceNet, "%s: closed ptp socket with id %d", __func__, idx + 1);
return 0;
}
int RelayTransport::PtpClose(int id) {
return ptp_close_postoffice(id - 1);
}
static int ptp_listen_postoffice(const SceNetEtherAddr *saddr, uint16_t sport, uint32_t bufsize, uint32_t rexmt_int, int rexmt_cnt) {
if (relayDisabled) {
return ERROR_NET_NO_SPACE;
}
// server connect delegated to accept
AdhocSocket *internal = (AdhocSocket *)malloc(sizeof(AdhocSocket));
if (internal == NULL) {
ERROR_LOG(Log::sceNet, "%s: out of heap memory when creating ptp listen socket", __func__);
return ERROR_NET_NO_SPACE;
}
memset(internal, 0, sizeof(AdhocSocket));
internal->retry_interval = rexmt_int;
internal->retry_count = rexmt_cnt;
internal->postofficeHandle = NULL;
internal->type = SOCK_PTP;
internal->data.ptp.laddr = *saddr;
internal->data.ptp.lport = sport;
internal->data.ptp.state = ADHOC_PTP_STATE_LISTEN;
internal->data.ptp.rcv_sb_cc = bufsize;
internal->data.ptp.snd_sb_cc = 0;
internal->flags = 0;
internal->connectThread = NULL;
internal->lastAttempt = 0;
internal->internalLastAttempt = 0;
AdhocSocket **slot = NULL;
int i;
i = FindFreeAdhocSocketSlot();
if (i < MAX_SOCKET) {
slot = &adhocSockets[i];
}
if (slot == NULL) {
ERROR_LOG(Log::sceNet, "%s: out of socket slots when creating adhoc ptp listen socket", __func__);
free(internal);
return ERROR_NET_NO_SPACE;
}
internal->data.ptp.id = AEMU_POSTOFFICE_ID_BASE + i;
*slot = internal;
ptp_listen_postoffice_recover(i);
INFO_LOG(Log::sceNet, "%s: created ptp listen socket with id %d %p", __func__, i + 1, internal->postofficeHandle);
return i + 1;
}
int RelayTransport::PtpListen(const SceNetEtherAddr *saddr, int sport, int bufsize, int rexmt_int, int rexmt_cnt, int backlog, bool isClient, int flag) {
return ptp_listen_postoffice(saddr, sport, bufsize, rexmt_int, rexmt_cnt);
}
int RelayTransport::PtpSend(int id, const char *data, int *len, int timeout, int flag) {
auto& ptpsocket = adhocSockets[id - 1]->data.ptp;
int sent = ptp_send_postoffice(id - 1, data, len);
if (sent == 0) {
hleEatMicro(122);
return 0;
}
if (sent == SOCKET_ERROR) {
ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
return SCE_NET_ADHOC_ERROR_DISCONNECTED;
}
// SCE_NET_ADHOC_ERROR_WOULD_BLOCK
if (flag)
return hleLogVerbose(Log::sceNet, SCE_NET_ADHOC_ERROR_WOULD_BLOCK, "would block");
// Simulate blocking behaviour with non-blocking socket
u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id;
return WaitBlockingAdhocSocket(threadSocketId, PTP_SEND, id, (void*)data, len, timeout, nullptr, nullptr, "ptp send");
}
int RelayTransport::PtpRecv(int id, void *buf, int *len, int timeout, int flag) {
auto& ptpsocket = adhocSockets[id - 1]->data.ptp;
int received = ptp_recv_postoffice(id - 1, buf, len);
if (received == 0) {
// we got data
DEBUG_LOG(Log::sceNet, "sceNetAdhocPtpRecv[%i:%u]: Result:%i (Error:%i)", id, ptpsocket.lport, received, 0);
hleEatMicro(50);
return 0;
}
if (received == SOCKET_ERROR) {
// the socket died, let the game know
ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
DEBUG_LOG(Log::sceNet, "sceNetAdhocPtpRecv[%i:%u]: Result:%i (Error:%i)", id, ptpsocket.lport, received, 0);
return SCE_NET_ADHOC_ERROR_DISCONNECTED;
}
// SCE_NET_ADHOC_ERROR_WOULD_BLOCK
if (flag == 0) {
// Simulate blocking behaviour with non-blocking socket
u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id;
return WaitBlockingAdhocSocket(threadSocketId, PTP_RECV, id, buf, len, timeout, nullptr, nullptr, "ptp recv");
}
return hleLogVerbose(Log::sceNet, SCE_NET_ADHOC_ERROR_WOULD_BLOCK, "would block");
}
int RelayTransport::PtpFlush(int socketId) {
// there's no manual flushing in the relay library, it's by default no delay there
return 0;
}
+3 -35
View File
@@ -54,6 +54,7 @@
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceNetAdhoc.h"
#include "Core/HLE/AdhocTransport.h"
#include "Core/Instance.h"
#include "proAdhoc.h"
@@ -97,7 +98,6 @@ int adhocConnectionType = ADHOC_CONNECT;
int gameModeSocket = (int)INVALID_SOCKET; // UDP/PDP socket? on Master only?
int gameModeBuffSize = 0;
u8* gameModeBuffer = nullptr;
extern bool serverHasRelay;
GameModeArea masterGameModeArea;
std::vector<GameModeArea> replicaGameModeAreas;
std::vector<SceNetEtherAddr> requiredGameModeMacs;
@@ -446,39 +446,7 @@ void deleteAllAdhocSockets() {
if (adhocSockets[i] != NULL) {
auto sock = adhocSockets[i];
if (serverHasRelay) {
if (sock->type == SOCK_PTP) {
// sync
if (sock->connectThread != NULL) {
sock->connectThread->join();
delete sock->connectThread;
}
void *socket = sock->postofficeHandle;
if (socket != NULL) {
if (sock->data.ptp.state == ADHOC_PTP_STATE_LISTEN) {
ptp_listen_close(socket);
} else {
ptp_close(socket);
}
}
} else {
if (sock->postofficeHandle != NULL) {
pdp_delete(sock->postofficeHandle);
}
}
} else {
int fd = -1;
if (sock->type == SOCK_PTP)
fd = sock->data.ptp.id;
else if (sock->type == SOCK_PDP)
fd = sock->data.pdp.id;
if (fd > 0) {
// Close Socket
shutdown(fd, SD_RECEIVE);
closesocket(fd);
}
}
Transport().CloseHostSocket(sock);
// Free Memory
free(adhocSockets[i]);
@@ -1521,7 +1489,7 @@ static void StartAdhocctlLogin() {
// If Server is at localhost and we are in P2P mode, try to bind socket to specific adapter before connecting to prevent 2nd instance being recognized as already existing 127.0.0.1 by adhocctl server
// (may not works in WinXP/2003 for IPv4 due to "Weak End System" model)
struct adhocctl_addr_v4 bindAddr = {0};
const bool bind = isLocalServer && !serverHasRelay;
const bool bind = isLocalServer && Transport().BindServerConnectionToLocalIp();
if (bind) {
bindAddr.ip = g_localhostIP.in.sin_addr.s_addr;
bindAddr.port = 0;
+74 -2029
View File
File diff suppressed because it is too large. Load diff
+6 -2
View File
@@ -396,10 +396,14 @@ def screen_matches(args, image):
want = bytes.fromhex(args.color)
have = image.convert("RGB").getpixel((x, y))
return max(abs(a - b) for a, b in zip(have, want)) <= args.tolerance
ref = Image.open(script_path(args, args.ref)).convert("RGB")
ref = Image.open(script_path(args, args.ref))
# A reference with an alpha channel is compared only where it's opaque: text over a moving
# background, say, with only the letters kept.
mask = ref.getchannel("A").point(lambda a: 255 if a >= 128 else 0) if "A" in ref.getbands() else None
ref = ref.convert("RGB")
x, y = parse_ints(args.box, 2)[:2] if args.box else (0, 0)
region = image.convert("RGB").crop((x, y, x + ref.width, y + ref.height))
diff = ImageStat.Stat(ImageChops.difference(region, ref)).mean
diff = ImageStat.Stat(ImageChops.difference(region, ref), mask).mean
return sum(diff) / len(diff) <= args.tolerance
+3
View File
@@ -182,6 +182,7 @@
<ClInclude Include="..\..\Core\HLE\KUBridge.h" />
<ClInclude Include="..\..\Core\HLE\sceSysreg.h" />
<ClInclude Include="..\..\Core\HLE\PopsBoot.h" />
<ClInclude Include="..\..\Core\HLE\AdhocTransport.h" />
<ClInclude Include="..\..\Core\HLE\proAdhoc.h" />
<ClInclude Include="..\..\Core\HLE\proAdhocServer.h" />
<ClInclude Include="..\..\Core\HLE\ReplaceTables.h" />
@@ -536,6 +537,8 @@
<ClCompile Include="..\..\Core\HLE\sceNet.cpp" />
<ClCompile Include="..\..\Core\HLE\sceNet_lib.cpp" />
<ClCompile Include="..\..\Core\HLE\NetAdhocCommon.cpp" />
<ClCompile Include="..\..\Core\HLE\AdhocTransportP2P.cpp" />
<ClCompile Include="..\..\Core\HLE\AdhocTransportRelay.cpp" />
<ClCompile Include="..\..\Core\HLE\sceNetAdhoc.cpp" />
<ClCompile Include="..\..\Core\HLE\sceNetAdhocMatching.cpp" />
<ClCompile Include="..\..\Core\HLE\sceNp.cpp" />
+3
View File
@@ -140,6 +140,8 @@
<ClCompile Include="..\..\Core\HLE\sceNet.cpp" />
<ClCompile Include="..\..\Core\HLE\sceNet_lib.cpp" />
<ClCompile Include="..\..\Core\HLE\NetAdhocCommon.cpp" />
<ClCompile Include="..\..\Core\HLE\AdhocTransportP2P.cpp" />
<ClCompile Include="..\..\Core\HLE\AdhocTransportRelay.cpp" />
<ClCompile Include="..\..\Core\HLE\sceNetAdhoc.cpp" />
<ClCompile Include="..\..\Core\HLE\sceNetAdhocMatching.cpp" />
<ClCompile Include="..\..\Core\HLE\sceNp.cpp" />
@@ -512,6 +514,7 @@
<ClInclude Include="..\..\Core\HLE\KUBridge.h" />
<ClInclude Include="..\..\Core\HLE\sceSysreg.h" />
<ClInclude Include="..\..\Core\HLE\PopsBoot.h" />
<ClInclude Include="..\..\Core\HLE\AdhocTransport.h" />
<ClInclude Include="..\..\Core\HLE\proAdhoc.h" />
<ClInclude Include="..\..\Core\HLE\proAdhocServer.h" />
<ClInclude Include="..\..\Core\HLE\ReplaceTables.h" />
+2
View File
@@ -808,6 +808,8 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/HLE/proAdhoc.cpp \
$(SRC)/Core/HLE/proAdhocServer.cpp \
$(SRC)/Core/HLE/NetAdhocCommon.cpp \
$(SRC)/Core/HLE/AdhocTransportP2P.cpp \
$(SRC)/Core/HLE/AdhocTransportRelay.cpp \
$(SRC)/Core/HLE/sceNetAdhoc.cpp \
$(SRC)/ext/aemu_postoffice/client/postoffice.c \
$(SRC)/ext/aemu_postoffice/client/postoffice_mem_stdc.c \
+2
View File
@@ -829,6 +829,8 @@ SOURCES_CXX += \
$(COREDIR)/HLE/sceNet.cpp \
$(COREDIR)/HLE/sceNet_lib.cpp \
$(COREDIR)/HLE/NetAdhocCommon.cpp \
$(COREDIR)/HLE/AdhocTransportP2P.cpp \
$(COREDIR)/HLE/AdhocTransportRelay.cpp \
$(COREDIR)/HLE/sceNetAdhoc.cpp \
$(COREDIR)/HLE/sceNetAdhocMatching.cpp \
$(COREDIR)/HLE/sceNetApctl.cpp \