Merge pull request #15456 from ANR2ME/adhoc

[Adhoc] Fix inconsistency issue between Windows and non-Windows
This commit is contained in:
Henrik Rydgård 2022-03-24 14:24:43 +01:00 committed by GitHub
commit 6f04f52f5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 263 additions and 124 deletions

View File

@ -228,6 +228,8 @@ void addFriend(SceNetAdhocctlConnectPacketS2C * packet) {
peer->nickname = packet->name;
peer->mac_addr = packet->mac;
peer->ip_addr = packet->ip;
// Calculate final IP-specific Port Offset
peer->port_offset = ((isOriPort && !isPrivateIP(peer->ip_addr)) ? 0 : portOffset);
// Update TimeStamp
peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
}
@ -248,6 +250,9 @@ void addFriend(SceNetAdhocctlConnectPacketS2C * packet) {
// Save IP Address
peer->ip_addr = packet->ip;
// Calculate final IP-specific Port Offset
peer->port_offset = ((isOriPort && !isPrivateIP(peer->ip_addr)) ? 0 : portOffset);
// TimeStamp
peer->last_recv = CoreTiming::GetGlobalTimeUsScaled();
@ -1795,11 +1800,12 @@ int getLocalIp(sockaddr_in* SocketAddress) {
#if !PPSSPP_PLATFORM(SWITCH)
if (metasocket != (int)INVALID_SOCKET) {
struct sockaddr_in localAddr;
struct sockaddr_in localAddr {};
localAddr.sin_addr.s_addr = INADDR_ANY;
socklen_t addrLen = sizeof(localAddr);
int ret = getsockname((int)metasocket, (struct sockaddr*)&localAddr, &addrLen);
if (SOCKET_ERROR != ret) {
// Note: Sometimes metasocket still contains a valid socket fd right after failed to connect to AdhocServer on a different thread, thus ended with 0.0.0.0 here
if (SOCKET_ERROR != ret && localAddr.sin_addr.s_addr != 0) {
SocketAddress->sin_addr = localAddr.sin_addr;
return 0;
}
@ -1807,27 +1813,8 @@ int getLocalIp(sockaddr_in* SocketAddress) {
#endif // !PPSSPP_PLATFORM(SWITCH)
// Fallback if not connected to AdhocServer
#if defined(_WIN32)
// Get local host name
char szHostName[256] = "";
if (::gethostname(szHostName, sizeof(szHostName))) {
// Error handling
}
// Get local network IP addresses (LAN/VPN/loopback)
struct addrinfo hints, * res = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; // AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_ADDRCONFIG; // getaddrinfo with AI_ADDRCONFIG will fail when there is no local network connected? https://github.com/stephane/libmodbus/issues/575
// Note: getaddrinfo could cause freezes on Android if there is no network https://github.com/hrydgard/ppsspp/issues/13300
if (getaddrinfo(szHostName, NULL, &hints, &res) == 0 && res != NULL) {
memcpy(&SocketAddress->sin_addr, &((struct sockaddr_in*)res->ai_addr)->sin_addr, sizeof(SocketAddress->sin_addr));
freeaddrinfo(res);
return 0;
}
#elif defined(getifaddrs) // On Android: Requires __ANDROID_API__ >= 24
// getifaddrs first appeared in glibc 2.3, On Android officially supported since __ANDROID_API__ >= 24
#if defined(_IFADDRS_H_) || (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3) || (__ANDROID_API__ >= 24)
struct ifaddrs* ifAddrStruct = NULL;
struct ifaddrs* ifa = NULL;
@ -1852,15 +1839,16 @@ int getLocalIp(sockaddr_in* SocketAddress) {
if (sock != SOCKET_ERROR) {
const char* kGoogleDnsIp = "8.8.8.8"; // Needs to be an IP string so it can be resolved as fast as possible to IP, doesn't need to be reachable
uint16_t kDnsPort = 53;
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
struct sockaddr_in serv {};
u32 ipv4 = INADDR_NONE; // inet_addr(kGoogleDnsIp); // deprecated?
inet_pton(AF_INET, kGoogleDnsIp, &ipv4);
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
serv.sin_addr.s_addr = ipv4;
serv.sin_port = htons(kDnsPort);
int err = connect(sock, (struct sockaddr*)&serv, sizeof(serv));
int err = connect(sock, (struct sockaddr*)&serv, sizeof(serv)); // connect should succeed even with SOCK_DGRAM
if (err != SOCKET_ERROR) {
struct sockaddr_in name;
struct sockaddr_in name {};
socklen_t namelen = sizeof(name);
err = getsockname(sock, (struct sockaddr*)&name, &namelen);
if (err != SOCKET_ERROR) {
@ -1876,7 +1864,7 @@ int getLocalIp(sockaddr_in* SocketAddress) {
}
uint32_t getLocalIp(int sock) {
struct sockaddr_in localAddr;
struct sockaddr_in localAddr {};
localAddr.sin_addr.s_addr = INADDR_ANY;
socklen_t addrLen = sizeof(localAddr);
getsockname(sock, (struct sockaddr*)&localAddr, &addrLen);
@ -1887,7 +1875,7 @@ uint32_t getLocalIp(int sock) {
}
static std::vector<std::pair<uint32_t, uint32_t>> InitPrivateIPRanges() {
struct sockaddr_in saNet, saMask;
struct sockaddr_in saNet {}, saMask{};
std::vector<std::pair<uint32_t, uint32_t>> ip_ranges;
if (1 == inet_pton(AF_INET, "192.168.0.0", &(saNet.sin_addr)) && 1 == inet_pton(AF_INET, "255.255.0.0", &(saMask.sin_addr)))
@ -1906,7 +1894,7 @@ static std::vector<std::pair<uint32_t, uint32_t>> InitPrivateIPRanges() {
bool isPrivateIP(uint32_t ip) {
static const std::vector<std::pair<uint32_t, uint32_t>> ip_ranges = InitPrivateIPRanges();
for (auto ipRange : ip_ranges) {
for (auto& ipRange : ip_ranges) {
if ((ip & ipRange.second) == (ipRange.first & ipRange.second)) // We can just use ipRange.first directly if it's already correctly formatted
return true;
}
@ -1934,7 +1922,7 @@ void getLocalMac(SceNetEtherAddr * addr){
}
uint16_t getLocalPort(int sock) {
struct sockaddr_in localAddr;
struct sockaddr_in localAddr {};
localAddr.sin_port = 0;
socklen_t addrLen = sizeof(localAddr);
getsockname(sock, (struct sockaddr*)&localAddr, &addrLen);
@ -2001,6 +1989,15 @@ int setSockTimeout(int sock, int opt, unsigned long timeout_usec) { // opt = SO_
return setsockopt(sock, SOL_SOCKET, opt, (char*)&optval, sizeof(optval));
}
int getSockError(int sock) {
int result = 0;
socklen_t result_len = sizeof(result);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&result, &result_len) < 0) {
result = errno;
}
return result;
}
int getSockNoDelay(int tcpsock) {
int opt = 0;
socklen_t optlen = sizeof(opt);
@ -2195,15 +2192,16 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
// Don't need to connect if AdhocServer DNS was not resolved
if (g_adhocServerIP.in.sin_addr.s_addr == INADDR_NONE)
return -1;
return SOCKET_ERROR;
// Don't need to connect if AdhocServer IP is the same with this instance localhost IP and having AdhocServer disabled
if (g_adhocServerIP.in.sin_addr.s_addr == g_localhostIP.in.sin_addr.s_addr && !g_Config.bEnableAdhocServer)
return -1;
return SOCKET_ERROR;
// Connect to Adhoc Server
int errorcode = 0;
int cnt = 0;
DEBUG_LOG(SCENET, "InitNetwork: Connecting to AdhocServer");
iResult = connect((int)metasocket, &g_adhocServerIP.addr, sizeof(g_adhocServerIP));
errorcode = errno;
@ -2211,8 +2209,10 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
u64 startTime = (u64)(time_now_d() * 1000000.0);
while (IsSocketReady((int)metasocket, false, true) <= 0) {
u64 now = (u64)(time_now_d() * 1000000.0);
if (coreState == CORE_POWERDOWN) return iResult;
if (now - startTime > adhocDefaultTimeout) break;
if (coreState == CORE_POWERDOWN)
return iResult;
if (now - startTime > (getSockError((int)metasocket) == EINPROGRESS ? adhocDefaultTimeout*2LL: adhocDefaultTimeout))
break;
sleep_ms(10);
}
if (IsSocketReady((int)metasocket, false, true) <= 0) {
@ -2233,6 +2233,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){
memcpy(packet.game.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN);
IsSocketReady((int)metasocket, false, true, nullptr, adhocDefaultTimeout);
DEBUG_LOG(SCENET, "InitNetwork: Sending LOGIN OPCODE %d", packet.base.opcode);
int sent = send((int)metasocket, (char*)&packet, sizeof(packet), MSG_NOSIGNAL);
if (sent > 0) {
socklen_t addrLen = sizeof(LocalIP);
@ -2286,7 +2287,7 @@ bool resolveIP(uint32_t ip, SceNetEtherAddr * mac) {
return false;
}
bool resolveMAC(SceNetEtherAddr * mac, uint32_t * ip) {
bool resolveMAC(SceNetEtherAddr* mac, uint32_t* ip, u16* port_offset) {
// Get Local MAC Address
SceNetEtherAddr localMac;
getLocalMac(&localMac);
@ -2296,6 +2297,8 @@ bool resolveMAC(SceNetEtherAddr * mac, uint32_t * ip) {
sockaddr_in sockAddr;
getLocalIp(&sockAddr);
*ip = sockAddr.sin_addr.s_addr;
if (port_offset)
*port_offset = portOffset;
return true; // return succes
}
@ -2311,7 +2314,8 @@ bool resolveMAC(SceNetEtherAddr * mac, uint32_t * ip) {
if (isMacMatch(&peer->mac_addr, mac)) {
// Copy Data
*ip = peer->ip_addr;
if (port_offset)
*port_offset = peer->port_offset;
// Return Success
return true;
}

View File

@ -61,6 +61,7 @@
#undef ECONNABORTED
#undef ECONNRESET
#undef ECONNREFUSED
#undef ENETUNREACH
#undef ENOTCONN
#undef EAGAIN
#undef EINPROGRESS
@ -73,6 +74,7 @@
#define ECONNABORTED WSAECONNABORTED
#define ECONNRESET WSAECONNRESET
#define ECONNREFUSED WSAECONNREFUSED
#define ENETUNREACH WSAENETUNREACH
#define ENOTCONN WSAENOTCONN
#define EAGAIN WSAEWOULDBLOCK
#define EINPROGRESS WSAEWOULDBLOCK
@ -294,11 +296,12 @@ typedef struct SceNetAdhocctlPeerInfo {
SceNetAdhocctlPeerInfo * next;
SceNetAdhocctlNickname nickname;
SceNetEtherAddr mac_addr;
u16_le padding;
u16_le padding; // a copy of the padding(?) from SceNetAdhocctlPeerInfoEmu
u32_le flags;
u64_le last_recv; // Need to use the same method with sceKernelGetSystemTimeWide (ie. CoreTiming::GetGlobalTimeUsScaled) to prevent timing issue (ie. in game timeout)
u32_le ip_addr; // internal use only
u16_le port_offset; // IP-specific port offset (internal use only)
} PACK SceNetAdhocctlPeerInfo;
// Peer Information with u32 pointers
@ -306,7 +309,7 @@ typedef struct SceNetAdhocctlPeerInfoEmu {
u32_le next; // Changed the pointer to u32
SceNetAdhocctlNickname nickname;
SceNetEtherAddr mac_addr;
u16_le padding; //00 00
u16_le padding; //00 00 // Note: Not sure whether this is really padding or reserved/unknown field
u32_le flags; //00 04 00 00 on KHBBS and FF FF FF FF on Ys vs. Sora no Kiseki // State of the peer? Or related to sceNetAdhocAuth_CF4D9BED ?
u64_le last_recv; // Need to use the same method with sceKernelGetSystemTimeWide (ie. CoreTiming::GetGlobalTimeUsScaled) to prevent timing issue (ie. in game timeout)
} PACK SceNetAdhocctlPeerInfoEmu;
@ -394,6 +397,7 @@ typedef struct AdhocSocket {
s32 retry_count; // multiply with retry interval to be used as keepalive timeout
s32 attemptCount; // connect/accept attempts
u64 lastAttempt; // timestamp to retry again
bool isClient; // true if the game is using local port 0 when creating the socket
union {
SceNetAdhocPdpStat pdp;
SceNetAdhocPtpStat ptp;
@ -1336,6 +1340,11 @@ int setSockMSS(int sock, int size);
*/
int setSockTimeout(int sock, int opt, unsigned long timeout_usec);
/*
* Get Socket SO_ERROR (Requests and clears pending error information on the socket)
*/
int getSockError(int sock);
/*
* Get TCP Socket TCP_NODELAY (Nagle Algo)
*/
@ -1448,9 +1457,10 @@ bool resolveIP(uint32_t ip, SceNetEtherAddr * mac);
* Resolve MAC to IP
* @param mac Peer MAC Address
* @param ip OUT: Peer IP
* @param port_offset OUT: Peer IP-specific Port Offset
* @return true on success
*/
bool resolveMAC(SceNetEtherAddr * mac, uint32_t * ip);
bool resolveMAC(SceNetEtherAddr* mac, uint32_t* ip, u16* port_offset = nullptr);
/**
* Check whether Network Name contains only valid symbols

View File

@ -116,6 +116,7 @@ void sendBulkDataPacket(SceNetAdhocMatchingContext* context, SceNetEtherAddr* ma
int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEtherAddr* addr, u16_le* port);
int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock);
int FlushPtpSocket(int socketId);
int RecreatePtpSocket(int ptpId);
int NetAdhocGameMode_DeleteMaster();
int NetAdhocctl_ExitGameMode();
int NetAdhocPtp_Connect(int id, int timeout, int flag, bool allowForcedConnect = true);
@ -459,12 +460,13 @@ int StartGameModeScheduler() {
return 0;
}
int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) {
int DoBlockingPdpRecv(AdhocSocketRequest& req, s64& result) {
auto sock = adhocSockets[req.id - 1];
if (!sock) {
result = ERROR_NET_ADHOC_SOCKET_DELETED;
return 0;
}
auto& pdpsocket = sock->data.pdp;
if (sock->flags & ADHOC_F_ALERTRECV) {
result = ERROR_NET_ADHOC_SOCKET_ALERTED;
sock->alerted_flags |= ADHOC_F_ALERTRECV;
@ -481,7 +483,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) {
memset(&sin, 0, sinlen);
// On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative
ret = recvfrom(uid, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
ret = recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
sockerr = errno;
// Discard packets from IP that can't be translated into MAC address to prevent confusing the game, since the sender MAC won't be updated and may contains invalid/undefined value.
@ -493,7 +495,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) {
// Remove the packet from socket buffer
sinlen = sizeof(sin);
memset(&sin, 0, sinlen);
recvfrom(uid, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
// Try again later, until timeout reached
u64 now = (u64)(time_now_d() * 1000000.0);
if (req.timeout != 0 && now - req.startTime > req.timeout) {
@ -513,11 +515,11 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) {
if (ret >= 0 && ret <= *req.length) {
sinlen = sizeof(sin);
memset(&sin, 0, sinlen);
ret = recvfrom(uid, (char*)req.buffer, std::max(0, *req.length), MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
ret = recvfrom(pdpsocket.id, (char*)req.buffer, std::max(0, *req.length), MSG_NOSIGNAL, (struct sockaddr*)&sin, &sinlen);
// UDP can also receives 0 data, while on TCP receiving 0 data = connection gracefully closed, but not sure whether PDP can send/recv 0 data or not tho
*req.length = 0;
if (ret >= 0) {
DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", req.id, getLocalPort(uid), ret, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", req.id, getLocalPort(pdpsocket.id), ret, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
// Find Peer MAC
if (resolveIP(sin.sin_addr.s_addr, &mac)) {
@ -539,7 +541,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) {
*req.length = ret;
*req.remotePort = ntohs(sin.sin_port) - portOffset;
WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", req.id, getLocalPort(uid), ret, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", req.id, getLocalPort(pdpsocket.id), ret, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
}
}
result = 0;
@ -556,7 +558,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) {
}
// Returning required buffer size when available data in recv buffer is larger than provided buffer size
else if (ret > *req.length) {
WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", req.id, getLocalPort(uid), ret, *req.length, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", req.id, getLocalPort(pdpsocket.id), ret, *req.length, ip2str(sin.sin_addr).c_str(), ntohs(sin.sin_port));
*req.length = ret;
// Find Peer MAC
@ -583,7 +585,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) {
return 0;
}
int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTargets& targetPeers) {
int DoBlockingPdpSend(AdhocSocketRequest& req, s64& result, AdhocSendTargets& targetPeers) {
auto sock = adhocSockets[req.id - 1];
if (!sock) {
result = ERROR_NET_ADHOC_SOCKET_DELETED;
@ -600,16 +602,16 @@ int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTa
bool retry = false;
for (auto peer = targetPeers.peers.begin(); peer != targetPeers.peers.end(); ) {
// Fill in Target Structure
struct sockaddr_in target;
struct sockaddr_in target {};
target.sin_family = AF_INET;
target.sin_addr.s_addr = peer->ip;
target.sin_port = htons(peer->port + ((isOriPort && !isPrivateIP(peer->ip)) ? 0 : portOffset));
target.sin_port = htons(peer->port + peer->portOffset);
int ret = sendto(pdpsocket.id, (const char*)req.buffer, targetPeers.length, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target));
int sockerr = errno;
if (ret >= 0) {
DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](B): Sent %u bytes to %s:%u\n", uid, getLocalPort(pdpsocket.id), ret, ip2str(target.sin_addr).c_str(), ntohs(target.sin_port));
DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](B): Sent %u bytes to %s:%u\n", req.id, getLocalPort(pdpsocket.id), ret, ip2str(target.sin_addr).c_str(), ntohs(target.sin_port));
// Remove successfully sent to peer to prevent sending the same data again during a retry
peer = targetPeers.peers.erase(peer);
}
@ -627,7 +629,7 @@ int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTa
}
if (ret == SOCKET_ERROR)
DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u](B) [size=%i]", sockerr, uid, getLocalPort(pdpsocket.id), ntohs(target.sin_port), targetPeers.length);
DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u](B) [size=%i]", sockerr, req.id, getLocalPort(pdpsocket.id), ntohs(target.sin_port), targetPeers.length);
}
if (retry)
@ -636,7 +638,7 @@ int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTa
return 0;
}
int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) {
int DoBlockingPtpSend(AdhocSocketRequest& req, s64& result) {
auto sock = adhocSockets[req.id - 1];
if (!sock) {
result = ERROR_NET_ADHOC_SOCKET_DELETED;
@ -650,7 +652,7 @@ int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) {
}
// Send Data
int ret = send(uid, (const char*)req.buffer, *req.length, MSG_NOSIGNAL);
int ret = send(ptpsocket.id, (const char*)req.buffer, *req.length, MSG_NOSIGNAL);
int sockerr = errno;
// Success
@ -689,7 +691,7 @@ int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) {
return 0;
}
int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) {
int DoBlockingPtpRecv(AdhocSocketRequest& req, s64& result) {
auto sock = adhocSockets[req.id - 1];
if (!sock) {
result = ERROR_NET_ADHOC_SOCKET_DELETED;
@ -702,7 +704,7 @@ int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) {
return 0;
}
int ret = recv(uid, (char*)req.buffer, std::max(0, *req.length), MSG_NOSIGNAL);
int ret = recv(ptpsocket.id, (char*)req.buffer, std::max(0, *req.length), MSG_NOSIGNAL);
int sockerr = errno;
// Received Data. POSIX: May received 0 bytes when the remote peer already closed the connection.
@ -745,7 +747,7 @@ int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) {
return 0;
}
int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) {
int DoBlockingPtpAccept(AdhocSocketRequest& req, s64& result) {
auto sock = adhocSockets[req.id - 1];
if (!sock) {
result = ERROR_NET_ADHOC_SOCKET_DELETED;
@ -764,10 +766,10 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) {
int ret, sockerr;
// Check if listening socket is ready to accept
ret = IsSocketReady(uid, true, false, &sockerr);
ret = IsSocketReady(ptpsocket.id, true, false, &sockerr);
if (ret > 0) {
// Accept Connection
ret = accept(uid, (struct sockaddr*)&sin, &sinlen);
ret = accept(ptpsocket.id, (struct sockaddr*)&sin, &sinlen);
sockerr = errno;
}
@ -795,7 +797,7 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) {
return 0;
}
int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) {
int DoBlockingPtpConnect(AdhocSocketRequest& req, s64& result, AdhocSendTargets& targetPeer) {
auto sock = adhocSockets[req.id - 1];
if (!sock) {
result = ERROR_NET_ADHOC_SOCKET_DELETED;
@ -808,16 +810,41 @@ int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) {
return 0;
}
int sockerr;
// Wait for Connection (assuming "connect" has been called before)
int ret = IsSocketReady(uid, false, true, &sockerr);
int sockerr, ret;
// Try to connect again if the first attempt failed due to remote side was not listening yet (ie. ECONNREFUSED or ETIMEDOUT)
if (sock->attemptCount < 1) {
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = targetPeer.peers[0].ip;
sin.sin_port = htons(ptpsocket.pport + targetPeer.peers[0].portOffset);
// Try to Connect
int ret = connect(ptpsocket.id, (struct sockaddr*)&sin, sizeof(sin));
int sockerr = errno;
if (connectInProgress(sockerr)) {
sock->data.ptp.state = ADHOC_PTP_STATE_SYN_SENT;
sock->attemptCount = 1;
}
// On Windows you can call connect again using the same socket after ECONNREFUSED/ETIMEDOUT/ENETUNREACH error, but on non-Windows you'll need to recreate the socket first
else {
if (RecreatePtpSocket(req.id) < 0) {
WARN_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Failed to Recreate Socket", req.id, ptpsocket.lport);
}
}
}
// Check if the connection has completed (assuming "connect" has been called before)
ret = IsSocketReady(ptpsocket.id, false, true, &sockerr);
// Connection is ready
if (ret > 0) {
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
socklen_t sinlen = sizeof(sin);
getpeername(uid, (struct sockaddr*)&sin, &sinlen);
ret = getpeername(ptpsocket.id, (struct sockaddr*)&sin, &sinlen);
if (ret == SOCKET_ERROR) {
WARN_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: getpeername error %i", req.id, ptpsocket.lport, errno);
}
// Set Connected State
ptpsocket.state = ADHOC_PTP_STATE_ESTABLISHED;
@ -838,7 +865,7 @@ int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) {
if (sock->nonblocking)
result = ERROR_NET_ADHOC_WOULD_BLOCK;
else
result = ERROR_NET_ADHOC_TIMEOUT; // FIXME: PSP never returned ERROR_NET_ADHOC_TIMEOUT on PtpConnect? or only returned ERROR_NET_ADHOC_TIMEOUT when the host is too busy? Seems to be returning ERROR_NET_ADHOC_CONNECTION_REFUSED on timedout instead (if the other side in not listening yet).
result = ERROR_NET_ADHOC_TIMEOUT; // FIXME: PSP never returned ERROR_NET_ADHOC_TIMEOUT on PtpConnect? or only returned ERROR_NET_ADHOC_TIMEOUT when the host is too busy? Seems to be returning ERROR_NET_ADHOC_CONNECTION_REFUSED on timedout instead (if the other side in not listening yet, which is similar to BSD).
}
}
// Select was interrupted or contains invalid args?
@ -851,7 +878,7 @@ int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) {
return 0;
}
int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) {
int DoBlockingPtpFlush(AdhocSocketRequest& req, s64& result) {
auto sock = adhocSockets[req.id - 1];
if (!sock) {
result = ERROR_NET_ADHOC_SOCKET_DELETED;
@ -865,7 +892,7 @@ int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) {
}
// Try Sending Empty Data
int sockerr = FlushPtpSocket(uid);
int sockerr = FlushPtpSocket(ptpsocket.id);
result = 0;
if (sockerr == EAGAIN || sockerr == EWOULDBLOCK) {
@ -876,13 +903,6 @@ int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) {
else
result = ERROR_NET_ADHOC_TIMEOUT;
}
else if (isDisconnected(sockerr)) {
// Change Socket State. // FIXME: Does Alerted Socket should be closed too?
ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
// Disconnected
result = ERROR_NET_ADHOC_DISCONNECTED;
}
if (sockerr != 0) {
DEBUG_LOG(SCENET, "sceNetAdhocPtpFlush[%i]: Socket Error (%i)", req.id, sockerr);
@ -891,7 +911,7 @@ int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) {
return 0;
}
int DoBlockingAdhocPollSocket(int uid, AdhocSocketRequest& req, s64& result) {
int DoBlockingAdhocPollSocket(AdhocSocketRequest& req, s64& result) {
SceNetAdhocPollSd* sds = (SceNetAdhocPollSd*)req.buffer;
int ret = PollAdhocSocket(sds, req.id, 0, 0);
if (ret <= 0) {
@ -953,7 +973,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {
result = 0;
break;
}
if (DoBlockingPdpSend(uid, req, result, sendTargetPeers[userdata])) {
if (DoBlockingPdpSend(req, result, sendTargetPeers[userdata])) {
// Try again in another 0.5ms until data available or timedout.
CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
return;
@ -962,7 +982,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {
break;
case PDP_RECV:
if (DoBlockingPdpRecv(uid, req, result)) {
if (DoBlockingPdpRecv(req, result)) {
// Try again in another 0.5ms until data available or timedout.
CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
return;
@ -970,7 +990,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {
break;
case PTP_SEND:
if (DoBlockingPtpSend(uid, req, result)) {
if (DoBlockingPtpSend(req, result)) {
// Try again in another 0.5ms until data available or timedout.
CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
return;
@ -978,7 +998,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {
break;
case PTP_RECV:
if (DoBlockingPtpRecv(uid, req, result)) {
if (DoBlockingPtpRecv(req, result)) {
// Try again in another 0.5ms until data available or timedout.
CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
return;
@ -986,7 +1006,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {
break;
case PTP_ACCEPT:
if (DoBlockingPtpAccept(uid, req, result)) {
if (DoBlockingPtpAccept(req, result)) {
// Try again in another 0.5ms until data available or timedout.
CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
return;
@ -994,7 +1014,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {
break;
case PTP_CONNECT:
if (DoBlockingPtpConnect(uid, req, result)) {
if (DoBlockingPtpConnect(req, result, sendTargetPeers[userdata])) {
// Try again in another 0.5ms until data available or timedout.
CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
return;
@ -1002,7 +1022,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {
break;
case PTP_FLUSH:
if (DoBlockingPtpFlush(uid, req, result)) {
if (DoBlockingPtpFlush(req, result)) {
// Try again in another 0.5ms until data available or timedout.
CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
return;
@ -1010,7 +1030,7 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) {
break;
case ADHOC_POLL_SOCKET:
if (DoBlockingAdhocPollSocket(uid, req, result)) {
if (DoBlockingAdhocPollSocket(req, result)) {
// Try again in another 0.5ms until data available or timedout.
CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata);
return;
@ -1330,6 +1350,7 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 f
// Library is initialized
SceNetEtherAddr * saddr = (SceNetEtherAddr *)mac;
bool isClient = false;
if (netAdhocInited) {
// Valid Arguments are supplied
if (mac != NULL && bufferSize > 0) {
@ -1340,7 +1361,10 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 f
}
//sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this
if (port == 0) port = -static_cast<int>(portOffset);
if (port == 0) {
isClient = true;
port = -static_cast<int>(portOffset);
}
// Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems :( So we try to fix it here
if (saddr != NULL) {
getLocalMac(saddr);
@ -1367,7 +1391,7 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 f
setUDPConnReset(usocket, false);
// Binding Information for local Port
struct sockaddr_in addr;
struct sockaddr_in addr {};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
if (isLocalServer) {
@ -1419,6 +1443,7 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 f
internal->type = SOCK_PDP;
internal->nonblocking = flag;
internal->buffer_size = bufferSize;
internal->isClient = isClient;
// Fill in Data
internal->data.pdp.id = usocket;
@ -1565,14 +1590,15 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int
// Single Target
if (!isBroadcastMAC(daddr)) {
// Fill in Target Structure
struct sockaddr_in target;
struct sockaddr_in target {};
target.sin_family = AF_INET;
target.sin_port = htons(dport + portOffset);
u16 finalPortOffset;
// Get Peer IP. Some games (ie. Vulcanus Seek and Destroy) seems to try to send to zero-MAC (ie. 00:00:00:00:00:00) first before sending to the actual destination MAC.. So may be sending to zero-MAC has a special meaning? (ie. to peek send buffer availability may be?)
if (resolveMAC((SceNetEtherAddr *)daddr, (uint32_t *)&target.sin_addr.s_addr)) {
if (resolveMAC((SceNetEtherAddr *)daddr, (uint32_t *)&target.sin_addr.s_addr, &finalPortOffset)) {
// Some games (ie. PSP2) might try to talk to it's self, not sure if they talked through WAN or LAN when using public Adhoc Server tho
target.sin_port = htons(dport + ((isOriPort && !isPrivateIP(target.sin_addr.s_addr)) ? 0 : portOffset));
target.sin_port = htons(dport + finalPortOffset);
// Acquire Network Lock
//_acquireNetworkLock();
@ -1591,7 +1617,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int
}
AdhocSendTargets dest = { len, {}, false };
dest.peers.push_back({ target.sin_addr.s_addr, dport });
dest.peers.push_back({ target.sin_addr.s_addr, dport, finalPortOffset });
sendTargetPeers[threadSocketId] = dest;
return WaitBlockingAdhocSocket(threadSocketId, PDP_SEND, id, data, nullptr, timeout, nullptr, nullptr, "pdp send");
}
@ -1652,7 +1678,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int
if (peer->last_recv == 0)
continue;
dest.peers.push_back({ peer->ip_addr, dport });
dest.peers.push_back({ peer->ip_addr, dport, peer->port_offset });
}
// Free Peer Lock
peerlock.unlock();
@ -1672,12 +1698,12 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int
// Non-blocking
else {
// Iterate Peers
for (auto peer : dest.peers) {
for (auto& peer : dest.peers) {
// Fill in Target Structure
struct sockaddr_in target;
struct sockaddr_in target {};
target.sin_family = AF_INET;
target.sin_addr.s_addr = peer.ip;
target.sin_port = htons(dport + ((isOriPort && !isPrivateIP(peer.ip)) ? 0 : portOffset));
target.sin_port = htons(dport + peer.portOffset);
int sent = sendto(pdpsocket.id, (const char*)data, len, MSG_NOSIGNAL, (struct sockaddr*)&target, sizeof(target));
int error = errno;
@ -2151,7 +2177,7 @@ int NetAdhocPdp_Delete(int id, int unknown) {
// Valid Socket
if (sock != NULL && sock->type == SOCK_PDP) {
// Close Connection
struct linger sl;
struct linger sl {};
sl.l_onoff = 1; // non-zero value enables linger option in kernel
sl.l_linger = 0; // timeout interval in seconds
setsockopt(sock->data.pdp.id, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl));
@ -3223,6 +3249,92 @@ static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) {
}
int RecreatePtpSocket(int ptpId) {
auto sock = adhocSockets[ptpId - 1];
if (!sock) {
return ERROR_NET_ADHOC_SOCKET_ID_NOT_AVAIL;
}
// Close old socket
struct linger sl {};
sl.l_onoff = 1; // non-zero value enables linger option in kernel
sl.l_linger = 0; // timeout interval in seconds
setsockopt(sock->data.ptp.id, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl));
closesocket(sock->data.ptp.id);
// Create a new socket
int tcpsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Valid Socket produced
if (tcpsocket > 0) {
sock->data.ptp.id = tcpsocket;
// Change socket MSS
setSockMSS(tcpsocket, PSP_ADHOC_PTP_MSS);
// Change socket buffer size to be consistent on all platforms.
setSockBufferSize(tcpsocket, SO_SNDBUF, sock->buffer_size * 5); //PSP_ADHOC_PTP_MSS
setSockBufferSize(tcpsocket, SO_RCVBUF, sock->buffer_size * 10); //PSP_ADHOC_PTP_MSS*10
// Enable KeepAlive
setSockKeepAlive(tcpsocket, true, sock->retry_interval / 1000000L, sock->retry_count);
// Ignore SIGPIPE when supported (ie. BSD/MacOS)
setSockNoSIGPIPE(tcpsocket, 1);
// Enable Port Re-use
setSockReuseAddrPort(tcpsocket);
// Apply Default Send Timeout Settings to Socket
setSockTimeout(tcpsocket, SO_SNDTIMEO, sock->retry_interval);
// Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function?
setSockNoDelay(tcpsocket, 1);
// Binding Information for local Port
struct sockaddr_in addr {};
// addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
if (isLocalServer) {
getLocalIp(&addr);
}
uint16_t requestedport = static_cast<int>(sock->data.ptp.lport + static_cast<int>(portOffset));
// Avoid getting random port due to port offset when original port wasn't 0 (ie. original_port + port_offset = 65536 = 0)
if (requestedport == 0 && sock->data.ptp.lport > 0)
requestedport = 65535; // Hopefully it will be safe to default it to 65535 since there can't be more than one port that can bumped into 65536
addr.sin_port = htons(requestedport);
// Bound Socket to local Port
if (bind(tcpsocket, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
ERROR_LOG(SCENET, "RecreatePtpSocket(%i) - Socket error (%i) when binding port %u", ptpId, errno, ntohs(addr.sin_port));
}
else {
// Update sport with the port assigned internal->lport = ntohs(local.sin_port)
socklen_t len = sizeof(addr);
if (getsockname(tcpsocket, (struct sockaddr*)&addr, &len) == 0) {
uint16_t boundport = ntohs(addr.sin_port);
if (sock->data.ptp.lport + static_cast<int>(portOffset) >= 65536 || static_cast<int>(boundport) - static_cast<int>(portOffset) <= 0)
WARN_LOG(SCENET, "RecreatePtpSocket(%id) - Wrapped Port Detected: Original(%d) -> Requested(%d), Bound(%d) -> BoundOriginal(%d)", ptpId, sock->data.ptp.lport, requestedport, boundport, boundport - portOffset);
u16 newlport = boundport - portOffset;
if (newlport != sock->data.ptp.lport) {
WARN_LOG(SCENET, "RecreatePtpSocket(%id) - Old and New LPort is different! The port may need to be reforwarded");
if (!sock->isClient)
UPnP_Add(IP_PROTOCOL_TCP, isOriPort ? newlport : newlport + portOffset, newlport + portOffset);
}
sock->data.ptp.lport = newlport;
}
}
// Switch to non-blocking for futher usage
changeBlockingMode(tcpsocket, 1);
}
else
return ERROR_NET_ADHOC_SOCKET_ID_NOT_AVAIL;
return 0;
}
/**
* Adhoc Emulator PTP Active Socket Creator
* @param saddr Local MAC (Unused)
@ -3294,7 +3406,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac,
setSockNoDelay(tcpsocket, 1);
// Binding Information for local Port
struct sockaddr_in addr;
struct sockaddr_in addr {};
// addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
@ -3343,11 +3455,12 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac,
internal->retry_count = rexmt_cnt;
internal->nonblocking = flag;
internal->buffer_size = bufsize;
internal->isClient = isClient;
// Copy Infrastructure Socket ID
internal->data.ptp.id = tcpsocket;
// Copy Address Information
// Copy Address & Port Information
internal->data.ptp.laddr = *saddr;
internal->data.ptp.paddr = *daddr;
internal->data.ptp.lport = sport;
@ -3365,6 +3478,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac,
changeBlockingMode(tcpsocket, 1);
// Initiate PtpConnect (ie. The Warrior seems to try to PtpSend right after PtpOpen without trying to PtpConnect first)
// TODO: Need to handle ECONNREFUSED better on non-Windows, if there are games that never called PtpConnect and only relies on [blocking?] PtpOpen to get connected
NetAdhocPtp_Connect(i + 1, rexmt_int, 1, false);
// Workaround to give some time to get connected before returning from PtpOpen over high latency
@ -3454,6 +3568,8 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther
internal->attemptCount = 1; // Used to differentiate between closed state of disconnected socket and not connected yet.
internal->retry_interval = socket->retry_interval;
internal->retry_count = socket->retry_count;
internal->isClient = true;
// Enable KeepAlive
setSockKeepAlive(newsocket, true, internal->retry_interval / 1000000L, internal->retry_count);
@ -3651,11 +3767,12 @@ int NetAdhocPtp_Connect(int id, int timeout, int flag, bool allowForcedConnect)
// sin.sin_len = sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_port = htons(ptpsocket.pport + portOffset);
u16 finalPortOffset;
// Grab Peer IP
if (resolveMAC(&ptpsocket.paddr, (uint32_t*)&sin.sin_addr.s_addr)) {
if (resolveMAC(&ptpsocket.paddr, (uint32_t*)&sin.sin_addr.s_addr, &finalPortOffset)) {
// Some games (ie. PSP2) might try to talk to it's self, not sure if they talked through WAN or LAN when using public Adhoc Server tho
sin.sin_port = htons(ptpsocket.pport + ((isOriPort && !isPrivateIP(sin.sin_addr.s_addr)) ? 0 : portOffset));
sin.sin_port = htons(ptpsocket.pport + finalPortOffset);
// Connect Socket to Peer
// NOTE: Based on what i read at stackoverflow, The First Non-blocking POSIX connect will always returns EAGAIN/EWOULDBLOCK because it returns without waiting for ACK/handshake, But GvG Next Plus is treating non-blocking PtpConnect just like blocking connect, May be on a real PSP the first non-blocking sceNetAdhocPtpConnect can be successfull?
@ -3685,31 +3802,42 @@ int NetAdhocPtp_Connect(int id, int timeout, int flag, bool allowForcedConnect)
// Error handling
else if (connectresult == SOCKET_ERROR) {
// Connection in Progress
if (connectInProgress(errorcode)) {
// Connection in Progress, or
// ECONNREFUSED = No connection could be made because the target device actively refused it (on Windows/Linux/Android), or no one listening on the remote address (on Linux/Android) thus should try to connect again later (treated similarly to ETIMEDOUT/ENETUNREACH).
if (connectInProgress(errorcode) || errorcode == ECONNREFUSED || errorcode == ENETUNREACH) {
if (connectInProgress(errorcode))
{
socket->data.ptp.state = ADHOC_PTP_STATE_SYN_SENT;
socket->attemptCount++;
}
// On Windows you can call connect again using the same socket after ECONNREFUSED/ETIMEDOUT/ENETUNREACH error, but on non-Windows you'll need to recreate the socket first
else {
if (RecreatePtpSocket(id) < 0) {
WARN_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Failed to Recreate Socket", id, ptpsocket.lport);
}
}
socket->lastAttempt = CoreTiming::GetGlobalTimeUsScaled();
// Blocking Mode
// Workaround: Forcing first attempt to be blocking to prevent issue related to lobby or high latency networks. (can be useful for GvG Next Plus, Dissidia 012, and Fate Unlimited Codes)
if (!flag || (allowForcedConnect && g_Config.bForcedFirstConnect && socket->attemptCount == 1)) {
if (!flag || (allowForcedConnect && g_Config.bForcedFirstConnect && socket->attemptCount <= 1)) {
// Simulate blocking behaviour with non-blocking socket
u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id;
if (sendTargetPeers.find(threadSocketId) != sendTargetPeers.end()) {
DEBUG_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Socket(%d) is Busy!", id, ptpsocket.lport, ptpsocket.id);
return hleLogError(SCENET, ERROR_NET_ADHOC_BUSY, "busy?");
}
AdhocSendTargets dest = { 0, {}, false };
dest.peers.push_back({ sin.sin_addr.s_addr, ptpsocket.pport, finalPortOffset });
sendTargetPeers[threadSocketId] = dest;
return WaitBlockingAdhocSocket(threadSocketId, PTP_CONNECT, id, nullptr, nullptr, (flag) ? std::max((int)socket->retry_interval, timeout) : timeout, nullptr, nullptr, "ptp connect");
}
// NonBlocking Mode
else {
// Returning WOULD_BLOCK as Workaround for ERROR_NET_ADHOC_CONNECTION_REFUSED to be more cross-platform, since there is no way to simulate ERROR_NET_ADHOC_CONNECTION_REFUSED properly on Windows
return hleLogDebug(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block");
}
}
// No connection could be made because the target device actively refused it (on Windows/Linux/Android), or no one listening on the remote address (on Linux/Android).
else if (errorcode == ECONNREFUSED) {
// Workaround for ERROR_NET_ADHOC_CONNECTION_REFUSED to be more cross-platform, since there is no way to simulate ERROR_NET_ADHOC_CONNECTION_REFUSED properly on Windows
if (flag)
return hleLogError(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "connection refused workaround");
else
return hleLogError(SCENET, ERROR_NET_ADHOC_TIMEOUT, "connection refused workaround");
}
}
}
@ -3756,7 +3884,7 @@ int NetAdhocPtp_Close(int id, int unknown) {
// Valid Socket
if (socket != NULL && socket->type == SOCK_PTP) {
// Close Connection
struct linger sl;
struct linger sl {};
sl.l_onoff = 1; // non-zero value enables linger option in kernel
sl.l_linger = 0; // timeout interval in seconds
setsockopt(socket->data.ptp.id, SOL_SOCKET, SO_LINGER, (const char*)&sl, sizeof(sl));
@ -3822,6 +3950,7 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int
}
// Library is initialized
SceNetEtherAddr * saddr = (SceNetEtherAddr *)srcmac;
bool isClient = false;
if (netAdhocInited) {
// Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems :( So we try to fix it here
if (saddr != NULL) {
@ -3837,6 +3966,7 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int
// Random Port required
if (sport == 0) {
isClient = true;
//sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this
sport = -static_cast<int>(portOffset);
}
@ -3872,7 +4002,7 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int
setSockNoDelay(tcpsocket, 1);
// Binding Information for local Port
struct sockaddr_in addr;
struct sockaddr_in addr {};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
if (isLocalServer) {
@ -3922,11 +4052,12 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int
internal->retry_count = rexmt_cnt;
internal->nonblocking = flag;
internal->buffer_size = bufsize;
internal->isClient = isClient;
// Copy Infrastructure Socket ID
internal->data.ptp.id = tcpsocket;
// Copy Address Information
// Copy Address & Port Information
internal->data.ptp.laddr = *saddr;
internal->data.ptp.lport = sport;
@ -4262,13 +4393,6 @@ static int sceNetAdhocPtpFlush(int id, int timeout, int nonblock) {
u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | ptpsocket.id;
return WaitBlockingAdhocSocket(threadSocketId, PTP_FLUSH, id, nullptr, nullptr, timeout, nullptr, nullptr, "ptp flush");
}
else if (isDisconnected(error)) {
// Change Socket State
ptpsocket.state = ADHOC_PTP_STATE_CLOSED;
// Disconnected
return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "disconnected");
}
if (error != 0)
DEBUG_LOG(SCENET, "sceNetAdhocPtpFlush[%i:%u -> %s:%u]: Error:%i", id, ptpsocket.lport, mac2str(&ptpsocket.paddr).c_str(), ptpsocket.pport, error);
@ -4479,7 +4603,7 @@ static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) {
gmuinfo = (GameModeUpdateInfo*)Memory::GetPointer(infoAddr);
}
for (auto gma : replicaGameModeAreas) {
for (auto& gma : replicaGameModeAreas) {
if (gma.id == id) {
if (gma.data && gma.dataUpdated) {
Memory::Memcpy(gma.addr, gma.data, gma.size);
@ -6408,7 +6532,7 @@ void sendAcceptPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * ma
uint32_t siblingbuflen = 0;
// Parent Mode
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) siblingbuflen = sizeof(SceNetEtherAddr) * (countConnectedPeers(context) - 2);
if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) siblingbuflen = (u32)sizeof(SceNetEtherAddr) * (countConnectedPeers(context) - 2);
// Sibling Count
int siblingcount = siblingbuflen / sizeof(SceNetEtherAddr);

View File

@ -45,6 +45,7 @@ struct AdhocctlRequest {
struct AdhocSendTarget {
u32 ip;
u16 port; // original port
u16 portOffset; // port offset specific for this target IP
};
struct AdhocSendTargets {