mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 05:19:56 +00:00
Merge pull request #15456 from ANR2ME/adhoc
[Adhoc] Fix inconsistency issue between Windows and non-Windows
This commit is contained in:
commit
6f04f52f5c
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
socket->data.ptp.state = ADHOC_PTP_STATE_SYN_SENT;
|
||||
socket->attemptCount++;
|
||||
// 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);
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user