diff --git a/Common/Net/Resolve.cpp b/Common/Net/Resolve.cpp index 23d3bf989d..644de89310 100644 --- a/Common/Net/Resolve.cpp +++ b/Common/Net/Resolve.cpp @@ -54,6 +54,139 @@ void Shutdown() #endif } +bool HostPortExists(const std::string& host, int port, int timeout_ms) { + if (host.empty() || (port <= 0 || port > 65535) || timeout_ms < 0) return false; + + addrinfo hints; + addrinfo* res = nullptr; + + std::memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; // TCP + hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 + + int gai = getaddrinfo(host.c_str(), std::to_string(port).c_str(), &hints, &res); + if (gai != 0) { + // getaddrinfo failed (DNS resolve failed or bad port) + return false; + } + + bool ok = false; + + for (addrinfo* p = res; p != nullptr && !ok; p = p->ai_next) { + // create socket + int sockfd = +#ifdef _WIN32 + (int)socket(p->ai_family, p->ai_socktype, p->ai_protocol); +#else + socket(p->ai_family, p->ai_socktype, p->ai_protocol); +#endif + if (sockfd < 0) { + continue; + } + + // make non-blocking +#ifdef _WIN32 + unsigned long mode = 1; + ioctlsocket((SOCKET)sockfd, FIONBIO, &mode); +#else + int flags = fcntl(sockfd, F_GETFL, 0); + if (flags == -1) flags = 0; + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); +#endif + + // try connect + int conn = connect(sockfd, p->ai_addr, (int)p->ai_addrlen); +#ifdef _WIN32 + if (conn == 0) { + ok = true; // immediate success + } + else { + int err = WSAGetLastError(); + if (err == WSAEWOULDBLOCK || err == WSAEINPROGRESS) { + // fall through to select + } + else { + // immediate failure + } + } +#else + if (conn == 0) { + ok = true; // immediate success + } + else { + if (errno == EINPROGRESS) { + // fall through to select + } + else { + // immediate failure + } + } +#endif + + if (!ok) { + // wait for writable with timeout + fd_set writefds; + FD_ZERO(&writefds); +#ifdef _WIN32 + FD_SET((SOCKET)sockfd, &writefds); +#else + FD_SET(sockfd, &writefds); +#endif + + fd_set exceptfds; + FD_ZERO(&exceptfds); +#ifdef _WIN32 + FD_SET((SOCKET)sockfd, &exceptfds); +#else + FD_SET(sockfd, &exceptfds); +#endif + + timeval tv; + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + int sel = select( +#ifdef _WIN32 + 0, +#else + sockfd + 1, +#endif + nullptr, &writefds, &exceptfds, &tv); + + if (sel > 0) { + // check for error on socket + int sock_err = 0; + socklen_t len = sizeof(sock_err); +#ifdef _WIN32 + int ret = getsockopt((SOCKET)sockfd, SOL_SOCKET, SO_ERROR, (char*)&sock_err, &len); +#else + int ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &sock_err, &len); +#endif +#ifdef _WIN32 + bool writable = FD_ISSET(static_cast(sockfd), &writefds) != 0; +#else + bool writable = FD_ISSET(sockfd, &writefds) != 0; +#endif + + if (ret == 0 && sock_err == 0 && writable) { + ok = true; + } + } + // else timeout or error -> try next addr + } + + // close socket +#ifdef _WIN32 + closesocket((SOCKET)sockfd); +#else + close(sockfd); +#endif + } + + freeaddrinfo(res); + return ok; +} + // NOTE: Due to the nature of getaddrinfo, this can block indefinitely. Not good. bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error, DNSType type) { #if PPSSPP_PLATFORM(SWITCH) diff --git a/Common/Net/Resolve.h b/Common/Net/Resolve.h index 09c2ab168c..15e89cc9f1 100644 --- a/Common/Net/Resolve.h +++ b/Common/Net/Resolve.h @@ -18,6 +18,8 @@ enum class DNSType { IPV6 = 2, }; +bool HostPortExists(const std::string& host, int port, int timeout_ms); + bool DNSResolve(const std::string &host, const std::string &service, addrinfo **res, std::string &error, DNSType type = DNSType::ANY); void DNSResolveFree(addrinfo *res); bool GetLocalIP4List(std::vector& IP4s); diff --git a/Core/Config.cpp b/Core/Config.cpp index 480e69c29a..689d88ab74 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1011,7 +1011,7 @@ static const ConfigSetting controlSettings[] = { static const ConfigSetting networkSettings[] = { ConfigSetting("EnableWlan", SETTING(g_Config, bEnableWlan), false, CfgFlag::PER_GAME), ConfigSetting("EnableAdhocServer", SETTING(g_Config, bEnableAdhocServer), false, CfgFlag::PER_GAME), - ConfigSetting("proAdhocServer", SETTING(g_Config, proAdhocServer), "socom.cc", CfgFlag::PER_GAME), + ConfigSetting("proAdhocServer", SETTING(g_Config, sProAdhocServer), "socom.cc", CfgFlag::PER_GAME), ConfigSetting("proAdhocServerList", SETTING(g_Config, proAdhocServerList), &defaultProAdhocServerList, CfgFlag::DEFAULT), ConfigSetting("PortOffset", SETTING(g_Config, iPortOffset), 10000, CfgFlag::PER_GAME), ConfigSetting("PrimaryDNSServer", SETTING(g_Config, sInfrastructureDNSServer), "67.222.156.250", CfgFlag::PER_GAME), diff --git a/Core/Config.h b/Core/Config.h index df5798194f..eed525f59a 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -538,7 +538,7 @@ public: // Networking bool bEnableAdhocServer; - std::string proAdhocServer; + std::string sProAdhocServer; std::vector proAdhocServerList; std::string sInfrastructureDNSServer; std::string sInfrastructureUsername; // Username used for Infrastructure play. Different restrictions. diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index ae384fd0a6..b9d2f4e17b 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1353,9 +1353,9 @@ int friendFinder() { addrinfo* resolved = nullptr; std::string err; g_adhocServerIP.in.sin_addr.s_addr = INADDR_NONE; - if (g_Config.bEnableWlan && !net::DNSResolve(g_Config.proAdhocServer, "", &resolved, err)) { - ERROR_LOG(Log::sceNet, "DNS Error Resolving %s\n", g_Config.proAdhocServer.c_str()); - g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("DNS Error Resolving")) + g_Config.proAdhocServer); + if (g_Config.bEnableWlan && !net::DNSResolve(g_Config.sProAdhocServer, "", &resolved, err)) { + ERROR_LOG(Log::sceNet, "DNS Error Resolving %s\n", g_Config.sProAdhocServer.c_str()); + g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("DNS Error Resolving")) + g_Config.sProAdhocServer); } if (resolved) { for (auto ptr = resolved; ptr != NULL; ptr = ptr->ai_next) { @@ -2238,7 +2238,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ sleep_ms(10, "pro-adhoc-socket-poll"); } if (!done) { - ERROR_LOG(Log::sceNet, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), ip2str(g_adhocServerIP.in.sin_addr).c_str(), ntohs(g_adhocServerIP.in.sin_port)); + ERROR_LOG(Log::sceNet, "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.sProAdhocServer.c_str(), ip2str(g_adhocServerIP.in.sin_addr).c_str(), ntohs(g_adhocServerIP.in.sin_port)); g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to connect to Adhoc Server")) + " (" + std::string(n->T("Error")) + ": " + std::to_string(errorcode) + ")"); return iResult; } diff --git a/Core/HLE/proAdhocServer.cpp b/Core/HLE/proAdhocServer.cpp index 4560349fe0..c9b55b31d6 100644 --- a/Core/HLE/proAdhocServer.cpp +++ b/Core/HLE/proAdhocServer.cpp @@ -36,9 +36,11 @@ #include "Common/File/FileUtil.h" #include "Common/TimeUtil.h" +#include "Common/Net/Resolve.h" #include "Core/Util/PortManager.h" #include "Core/Instance.h" #include "Core/Core.h" +#include "Core/Config.h" #include "Core/HLE/proAdhocServer.h" #ifdef _WIN32 @@ -344,6 +346,8 @@ static const db_productid default_productids[] = { { "ULES01432", "Full Metal Alchemist - Brotherhood" }, { "ULUS10490", "GTA Chinatown Wars" }, { "ULUS10160", "GTA Vice City Stories" }, + { "ULUS10041", "GTA Liberty City Stories" }, + { "ULES00151", "GTA Liberty City Stories" }, { "ULUS10210", "Ghost Rider" }, { "ULJS00237", "God Eater" }, { "NPJH50832", "God Eater 2" }, @@ -462,6 +466,7 @@ static const db_productid default_productids[] = { { "ULJM05151", "Yu-Gi-Oh! GX Tag Force" }, { "ULJM05373", "Yu-Gi-Oh! GX Tag Force 3" }, { "NPUG80086", "flOw" }, + // TODO? GTA 10160,beta,10041,00151 }; // Function Prototypes @@ -1660,7 +1665,13 @@ int proAdhocServerThread(int port) // (int argc, char * argv[]) // Result int result = 0; - INFO_LOG(Log::sceNet, "AdhocServer: Begin of AdhocServer Thread"); + if (net::HostPortExists(g_Config.sProAdhocServer, SERVER_PORT, 200)) { + INFO_LOG(Log::sceNet, "AdhocServer: Skiped starting because the server is already available"); + return 0; + } + else { + INFO_LOG(Log::sceNet, "AdhocServer: Begin of AdhocServer Thread"); + } // Create Signal Receiver for CTRL + C //signal(SIGINT, interrupt); @@ -1816,11 +1827,6 @@ int create_listen_socket(uint16_t port) local.sin_addr.s_addr = INADDR_ANY; local.sin_port = htons(port); - // Should only bind to specific IP for the 2nd or more instance of PPSSPP to prevent communication interference issue when sharing the same port. (ie. Capcom Classics Collection Remixed) - if (PPSSPP_ID > 1) { - local.sin_addr = g_localhostIP.in.sin_addr; - } - // Bind Local Address to Socket int bindresult = bind(fd, (struct sockaddr *)&local, sizeof(local)); diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 4ad3332a27..e35f1485da 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -521,7 +521,7 @@ void InitLocalhostIP() { g_localhostIP.in.sin_addr.s_addr = htonl(localIP); g_localhostIP.in.sin_port = 0; - std::string serverStr(StripSpaces(g_Config.proAdhocServer)); + std::string serverStr(StripSpaces(g_Config.sProAdhocServer)); isLocalServer = (!strcasecmp(serverStr.c_str(), "localhost") || serverStr.find("127.") == 0); } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index f32a656ce4..094f7b9fe5 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1015,8 +1015,8 @@ void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSetti networkingSettings->Add(new ItemHeader(n->T("AdHoc server"))); networkingSettings->Add(new CheckBox(&g_Config.bEnableAdhocServer, n->T("Enable built-in PRO Adhoc Server", "Enable built-in PRO Adhoc Server"))); - networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address"), I18NCat::NONE))->OnClick.Add([=](UI::EventParams &) { - screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, &g_Config.proAdhocServerList, n->T("proAdhocServer Address:"))); + networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.sProAdhocServer, n->T("Change proAdhocServer Address"), I18NCat::NONE))->OnClick.Add([=](UI::EventParams &) { + screenManager()->push(new HostnameSelectScreen(&g_Config.sProAdhocServer, &g_Config.proAdhocServerList, n->T("proAdhocServer Address:"))); }); networkingSettings->Add(new SettingHint(n->T("Change proAdhocServer address hint"))); networkingSettings->Add(new ItemHeader(n->T("UPnP (port-forwarding)")));