Skip to content

Commit

Permalink
Merge pull request #14106 from ANR2ME/adhoc
Browse files Browse the repository at this point in the history
[Adhoc] Fix frozen (0 FPS) issue on Kao Challengers and Asterix & Obelix XX
  • Loading branch information
hrydgard committed Feb 11, 2021
2 parents 7095115 + 0ce2c2c commit 5c6251a
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 8 deletions.
9 changes: 3 additions & 6 deletions Core/HLE/proAdhoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ uint16_t portOffset;
uint32_t minSocketTimeoutUS;
uint32_t fakePoolSize = 0;
SceNetAdhocMatchingContext * contexts = NULL;
char* dummyPeekBuf64k = NULL;
int dummyPeekBuf64kSize = 65536;
int one = 1;
bool friendFinderRunning = false;
SceNetAdhocctlPeerInfo * friends = NULL;
Expand Down Expand Up @@ -1908,12 +1910,7 @@ u_long getAvailToRecv(int sock, int udpBufferSize) {
return 0;

if (udpBufferSize > 0 && n > 0) {
// TODO: Cap number of bytes of full DGRAM message(s) up to buffer size
char buf[8];
// Each recv can only received one message, not sure how to read the next pending msg without actually receiving the first one
err = recvfrom(sock, buf, sizeof(buf), MSG_PEEK | MSG_NOSIGNAL | MSG_TRUNC, NULL, NULL);
if (err >= 0)
return (u_long)err;
// TODO: Cap number of bytes of full DGRAM message(s) up to buffer size, but may cause Warriors Orochi 2 to get FPS drops
}
return n;
}
Expand Down
4 changes: 4 additions & 0 deletions Core/HLE/proAdhoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
#undef EISCONN
#undef EALREADY
#undef ETIMEDOUT
#undef EOPNOTSUPP
#define errno WSAGetLastError()
#define ESHUTDOWN WSAESHUTDOWN
#define ECONNABORTED WSAECONNABORTED
Expand All @@ -77,6 +78,7 @@
#define EISCONN WSAEISCONN
#define EALREADY WSAEALREADY
#define ETIMEDOUT WSAETIMEDOUT
#define EOPNOTSUPP WSAEOPNOTSUPP
inline bool connectInProgress(int errcode){ return (errcode == WSAEWOULDBLOCK || errcode == WSAEINPROGRESS || errcode == WSAEALREADY); }
inline bool isDisconnected(int errcode) { return (errcode == WSAECONNRESET || errcode == WSAECONNABORTED || errcode == WSAESHUTDOWN); }
#else
Expand Down Expand Up @@ -915,6 +917,8 @@ extern int defaultWlanChannel; // Default WLAN Channel for Auto, JPCSP uses 11

extern uint32_t fakePoolSize;
extern SceNetAdhocMatchingContext * contexts;
extern char* dummyPeekBuf64k;
extern int dummyPeekBuf64kSize;
extern int one;
extern bool friendFinderRunning;
extern SceNetAdhocctlPeerInfo * friends;
Expand Down
3 changes: 3 additions & 0 deletions Core/HLE/sceNet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ void __NetInit() {
g_adhocServerIP.in.sin_port = htons(SERVER_PORT); //27312 // Maybe read this from config too
g_adhocServerIP.in.sin_addr.s_addr = INADDR_NONE;

dummyPeekBuf64k = (char*)malloc(dummyPeekBuf64kSize);
InitLocalhostIP();

SceNetEtherAddr mac;
Expand Down Expand Up @@ -235,6 +236,8 @@ void __NetShutdown() {

// Since PortManager supposed to be general purpose for whatever port forwarding PPSSPP needed, may be we shouldn't clear & restore ports in here? it will be cleared and restored by PortManager's destructor when exiting PPSSPP anyway
__UPnPShutdown();

free(dummyPeekBuf64k);
}

static void __UpdateApctlHandlers(u32 oldState, u32 newState, u32 flag, u32 error) {
Expand Down
12 changes: 10 additions & 2 deletions Core/HLE/sceNetAdhoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,11 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) {
memset(&sin, 0, sizeof(sin));
socklen_t sinlen = sizeof(sin);

int ret = recvfrom(uid, (char*)req.buffer, *req.length, MSG_PEEK | MSG_NOSIGNAL | MSG_TRUNC, (sockaddr*)&sin, &sinlen);
// On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative
int ret = recvfrom(uid, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (sockaddr*)&sin, &sinlen);
int sockerr = errno;
if (ret > 0 && *req.length > 0)
memcpy(req.buffer, dummyPeekBuf64k, std::min(ret, *req.length));

// Note: UDP must not be received partially, otherwise leftover data in socket's buffer will be discarded
if (ret >= 0 && ret <= *req.length) {
Expand Down Expand Up @@ -483,6 +486,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) {
}
result = ERROR_NET_ADHOC_NOT_ENOUGH_SPACE;
}
// FIXME: Blocking operation with infinite timeout(0) should never get a TIMEOUT error, right? May be we should return INVALID_ARG instead if it was infinite timeout (0)?
else
result = ERROR_NET_ADHOC_TIMEOUT; // ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_DISCONNECTED

Expand Down Expand Up @@ -1689,7 +1693,11 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void *

// Receive Data. PDP always sent in full size or nothing(failed), recvfrom will always receive in full size as requested (blocking) or failed (non-blocking). If available UDP data is larger than buffer, excess data is lost.
// Should peek first for the available data size if it's more than len return ERROR_NET_ADHOC_NOT_ENOUGH_SPACE along with required size in len to prevent losing excess data
received = recvfrom(pdpsocket.id, (char*)buf, *len, MSG_PEEK | MSG_NOSIGNAL | MSG_TRUNC, (sockaddr*)&sin, &sinlen);
// On Windows: MSG_TRUNC are not supported on recvfrom (socket error WSAEOPNOTSUPP), so we use dummy buffer as an alternative
received = recvfrom(pdpsocket.id, dummyPeekBuf64k, dummyPeekBuf64kSize, MSG_PEEK | MSG_NOSIGNAL, (sockaddr*)&sin, &sinlen);
if (received > 0 && *len > 0)
memcpy(buf, dummyPeekBuf64k, std::min(received, *len));

if (received != SOCKET_ERROR && *len < received) {
WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, *len, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
*len = received;
Expand Down

0 comments on commit 5c6251a

Please sign in to comment.