Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adhoc fix + UPnP + Multiple Instance #13132

Closed
wants to merge 7 commits into from
506 changes: 249 additions & 257 deletions Core/HLE/proAdhoc.cpp

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions Core/HLE/proAdhoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ inline bool connectInProgress(int errcode){ return (errcode == EAGAIN || errcode
#define POLLPRI POLL_PRI
#endif

#ifndef SD_BOTH
#define SD_BOTH 0x02
#endif

#define IsMatch(buf1, buf2) (memcmp(&buf1, &buf2, sizeof(buf1)) == 0)

// Server Listening Port
Expand Down Expand Up @@ -789,7 +793,7 @@ class AfterAdhocMipsCall : public PSPAction {
AfterAdhocMipsCall() {}
static PSPAction* Create() { return new AfterAdhocMipsCall(); }
void DoState(PointerWrap& p) override {
auto s = p.Section("AfterAdhocMipsCall", 4, 4);
auto s = p.Section("AfterAdhocMipsCall", 3, 4);
if (!s)
return;

Expand Down Expand Up @@ -889,8 +893,7 @@ bool isPDPPortInUse(uint16_t port);
*/
bool isPTPPortInUse(uint16_t port);

char* mac2str(SceNetEtherAddr* mac);
char* mac2str(SceNetEtherAddr* mac, char* str, size_t size = 18);
std::string mac2str(SceNetEtherAddr* mac);

/*
* Matching Members
Expand Down
4 changes: 2 additions & 2 deletions Core/HLE/proAdhocServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1685,13 +1685,13 @@ int proAdhocServerThread(int port) // (int argc, char * argv[])
INFO_LOG(SCENET, "AdhocServer: Listening for Connections on TCP Port %u", port); //SERVER_PORT

// Port forward
g_PortManager.Add(IP_PROTOCOL_TCP, port);
UPnP_Add(IP_PROTOCOL_TCP, port); // g_PortManager.Add(IP_PROTOCOL_TCP, port);

// Enter Server Loop
result = server_loop(server);

// Remove Port mapping
g_PortManager.Remove(IP_PROTOCOL_TCP, port);
UPnP_Remove(IP_PROTOCOL_TCP, port); // g_PortManager.Remove(IP_PROTOCOL_TCP, port);

// Notify User
INFO_LOG(SCENET, "AdhocServer: Shutdown complete");
Expand Down
22 changes: 7 additions & 15 deletions Core/HLE/sceNet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,32 +123,24 @@ void __NetInit() {

InitLocalhostIP();

char tmpmac[18];
SceNetEtherAddr mac;
getLocalMac(&mac);
INFO_LOG(SCENET, "LocalHost IP will be %s [MAC: %s]", inet_ntoa(((sockaddr_in*)&LocalhostIP)->sin_addr), mac2str(&mac, tmpmac));

// Only initialize when UPnP is enabled since it takes a few seconds to detect UPnP device (may affect people who don't have UPnP device)
if (g_Config.bEnableUPnP) {
// TODO: May be we should initialize & cleanup somewhere else than here for PortManager to be used as general purpose for whatever port forwarding PPSSPP needed
g_PortManager.Initialize();
}
INFO_LOG(SCENET, "LocalHost IP will be %s [%s]", inet_ntoa(((sockaddr_in*)&LocalhostIP)->sin_addr), mac2str(&mac).c_str());

// TODO: May be we should initialize & cleanup somewhere else than here for PortManager to be used as general purpose for whatever port forwarding PPSSPP needed
__UPnPInit();

__ResetInitNetLib();
}

void __NetShutdown() {
// Network Cleanup
Net_Term();

__ResetInitNetLib();

// 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
if (g_PortManager.GetInitState() == UPNP_INITSTATE_DONE) {
g_PortManager.Clear();
g_PortManager.Restore();
g_PortManager.Terminate();
}
__UPnPShutdown();
}

static void __UpdateApctlHandlers(int oldState, int newState, int flag, int error) {
Expand Down Expand Up @@ -424,7 +416,7 @@ static void sceNetEtherStrton(u32 bufferPtr, u32 macPtr) {
}
}

VERBOSE_LOG(SCENET, "sceNetEtherStrton - [%s]", mac2str((SceNetEtherAddr*)Memory::GetPointer(macPtr)));
VERBOSE_LOG(SCENET, "sceNetEtherStrton - [%s]", mac2str((SceNetEtherAddr*)Memory::GetPointer(macPtr)).c_str());
// Seems to maybe kinda return the last value. Probably returns void.
//return value;
}
Expand Down
132 changes: 57 additions & 75 deletions Core/HLE/sceNetAdhoc.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Core/HLE/sceNetAdhoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ int sceNetAdhocctlCreate(const char * groupName);
// May need to use these from sceNet.cpp
extern bool netAdhocInited;
extern bool netAdhocctlInited;
extern bool networkInited;
extern int adhocDefaultTimeout;
extern int adhocEventPollDelayMS;
extern int adhocMatchingEventDelayMS;
Expand Down
97 changes: 94 additions & 3 deletions Core/Util/PortManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,18 @@
#include <Core/ELF/ParamSFO.h>
#include "Core/Util/PortManager.h"
#include <Common/Log.h>
#include <thread>
#include "i18n/i18n.h"
#include "net/resolve.h"
#include "thread/threadutil.h"
#include "base/timeutil.h"


PortManager g_PortManager;
bool upnpServiceRunning = false;
std::thread upnpServiceThread;
std::recursive_mutex upnpLock;
std::deque<UPnPArgs> upnpReqs;

PortManager::PortManager():
urls(0),
Expand Down Expand Up @@ -170,7 +177,7 @@ bool PortManager::Initialize(const unsigned int timeout) {

ERROR_LOG(SCENET, "PortManager - upnpDiscover failed (error: %i) or No UPnP device detected", error);
auto n = GetI18NCategory("Networking");
host->NotifyUserMessage(n->T("Unable to find UPnP device"), 6.0f, 0x0000ff);
host->NotifyUserMessage(n->T("Unable to find UPnP device"), 2.0f, 0x0000ff);
m_InitState = UPNP_INITSTATE_NONE;
return false;
}
Expand Down Expand Up @@ -216,7 +223,7 @@ bool PortManager::Add(const char* protocol, unsigned short port, unsigned short
ERROR_LOG(SCENET, "PortManager - AddPortMapping failed (error: %i)", r);
if (r == UPNPCOMMAND_HTTP_ERROR) {
auto n = GetI18NCategory("Networking");
host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 6.0f, 0x0000ff);
host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff);
Terminate(); // Most of the time errors occurred because the router is no longer reachable (ie. changed networks) so we should invalidate the state to prevent further lags due to timeouts
return false;
}
Expand Down Expand Up @@ -244,7 +251,7 @@ bool PortManager::Remove(const char* protocol, unsigned short port) {
ERROR_LOG(SCENET, "PortManager - DeletePortMapping failed (error: %i)", r);
if (r == UPNPCOMMAND_HTTP_ERROR) {
auto n = GetI18NCategory("Networking");
host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 6.0f, 0x0000ff);
host->NotifyUserMessage(n->T("UPnP need to be reinitialized"), 2.0f, 0x0000ff);
Terminate(); // Most of the time errors occurred because the router is no longer reachable (ie. changed networks) so we should invalidate the state to prevent further lags due to timeouts
return false;
}
Expand Down Expand Up @@ -403,3 +410,87 @@ bool PortManager::RefreshPortList() {
} while (r == 0);
return true;
}

int upnpService(const unsigned int timeout)
{
setCurrentThreadName("UPnPService");
INFO_LOG(SCENET, "UPnPService: Begin of UPnPService Thread");

// Service Loop
while (upnpServiceRunning && coreState != CORE_POWERDOWN) {
// Attempts to reconnect if not connected yet or got disconnected
if (g_Config.bEnableUPnP && g_PortManager.GetInitState() == UPNP_INITSTATE_NONE) {
g_PortManager.Initialize(timeout);
}

if (g_Config.bEnableUPnP && g_PortManager.GetInitState() == UPNP_INITSTATE_DONE && !upnpReqs.empty()) {
upnpLock.lock();
UPnPArgs arg = upnpReqs.front();
upnpLock.unlock();

bool ok = true;
switch (arg.cmd) {
case UPNP_CMD_ADD:
ok = g_PortManager.Add(arg.protocol.c_str(), arg.port, arg.intport);
break;
case UPNP_CMD_REMOVE:
ok = g_PortManager.Remove(arg.protocol.c_str(), arg.port);
break;
default:
break;
}

// It's only considered failed when disconnected (should be retried when reconnected)
if (ok) {
upnpLock.lock();
upnpReqs.pop_front();
upnpLock.unlock();
}
}

// Sleep for 1ms for faster response
sleep_ms(1);
}

// Cleaning up regardless of g_Config.bEnableUPnP to prevent lingering open ports on the router
if (g_PortManager.GetInitState() == UPNP_INITSTATE_DONE) {
g_PortManager.Clear();
g_PortManager.Restore();
g_PortManager.Terminate();
}

// Should we ingore any leftover UPnP requests? instead of processing it on the next game start
upnpLock.lock();
upnpReqs.clear();
upnpLock.unlock();

INFO_LOG(SCENET, "UPnPService: End of UPnPService Thread");
return 0;
}

void __UPnPInit(const unsigned int timeout) {
if (!upnpServiceRunning) {
upnpServiceRunning = true;
upnpServiceThread = std::thread(upnpService, timeout);
}
}

void __UPnPShutdown() {
if (upnpServiceRunning) {
upnpServiceRunning = false;
if (upnpServiceThread.joinable()) {
upnpServiceThread.join();
}
}
}

void UPnP_Add(const char* protocol, unsigned short port, unsigned short intport) {
std::lock_guard<std::recursive_mutex> upnpGuard(upnpLock);
upnpReqs.push_back({ UPNP_CMD_ADD, protocol, port, intport });
}

void UPnP_Remove(const char* protocol, unsigned short port) {
std::lock_guard<std::recursive_mutex> upnpGuard(upnpLock);
upnpReqs.push_back({ UPNP_CMD_REMOVE, protocol, port, port });
}

25 changes: 25 additions & 0 deletions Core/Util/PortManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,28 @@
#include <string>
#include <deque>

#ifdef _MSC_VER
#pragma pack(push,1)
#endif
typedef struct UPnPArgs {
int cmd;
std::string protocol;
unsigned short port;
unsigned short intport;
} PACK;
#ifdef _MSC_VER
#pragma pack(pop)
#endif

#define IP_PROTOCOL_TCP "TCP"
#define IP_PROTOCOL_UDP "UDP"
#define UPNP_INITSTATE_NONE 0
#define UPNP_INITSTATE_BUSY 1
#define UPNP_INITSTATE_DONE 2

#define UPNP_CMD_ADD 0
#define UPNP_CMD_REMOVE 1

struct UPNPUrls;
struct IGDdatas;

Expand Down Expand Up @@ -98,3 +114,12 @@ class PortManager {
};

extern PortManager g_PortManager;

void __UPnPInit(const unsigned int timeout = 2000);
void __UPnPShutdown();

// Add a port & protocol (TCP, UDP or vendor-defined) to map for forwarding (intport = 0 : same as [external] port)
void UPnP_Add(const char* protocol, unsigned short port, unsigned short intport = 0);

// Remove a port mapping (external port)
void UPnP_Remove(const char* protocol, unsigned short port);
71 changes: 60 additions & 11 deletions UI/GameSettingsScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ void GameSettingsScreen::CreateViews() {
networkingSettings->Add(new CheckBox(&g_Config.bEnableWlan, n->T("Enable networking", "Enable networking/wlan (beta)")));
networkingSettings->Add(new CheckBox(&g_Config.bDiscordPresence, n->T("Send Discord Presence information")));

networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeproAdhocServerAddress);
networkingSettings->Add(new ChoiceWithValueDisplay(&g_Config.proAdhocServer, n->T("Change proAdhocServer Address (localhost = multiple instance)"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeproAdhocServerAddress);
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.sMACAddress, n->T("Change Mac Address"), (const char *)nullptr))->OnClick.Handle(this, &GameSettingsScreen::OnChangeMacAddress);
static const char* wlanChannels[] = { "Auto", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11" };
Expand Down Expand Up @@ -1391,17 +1391,9 @@ UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) {
}

UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParams &e) {
auto sy = GetI18NCategory("System");
auto n = GetI18NCategory("Networking");

#if defined(__ANDROID__)
System_InputBoxGetString(sy->T("proAdhocServer Address:"), g_Config.proAdhocServer, [](bool result, const std::string &value) {
if (result) {
g_Config.proAdhocServer = value;
}
});
#else
screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, sy->T("proAdhocServer Address:")));
#endif
screenManager()->push(new HostnameSelectScreen(&g_Config.proAdhocServer, n->T("proAdhocServer Address:")));

return UI::EVENT_DONE;
}
Expand Down Expand Up @@ -1698,10 +1690,35 @@ void HostnameSelectScreen::CreatePopupContents(UI::ViewGroup *parent) {
buttonsRow1->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT)));

buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_LEFT)));
#if defined(__ANDROID__)
buttonsRow2->Add(new Button(di->T("Edit")))->OnClick.Handle(this, &HostnameSelectScreen::OnEditClick); // Since we don't have OnClick Event triggered from TextEdit's Touch().. here we go!
#endif
buttonsRow2->Add(new Button(di->T("Delete")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteClick);
buttonsRow2->Add(new Button(di->T("Delete all")))->OnClick.Handle(this, &HostnameSelectScreen::OnDeleteAllClick);
buttonsRow2->Add(new Button(di->T("Toggle List")))->OnClick.Handle(this, &HostnameSelectScreen::OnShowIPListClick);
buttonsRow2->Add(new Spacer(new LinearLayoutParams(1.0, G_RIGHT)));

std::vector<std::string> listIP = {"localhost","myneighborsushicat.com"};
net::GetIPList(listIP);
ipRows_ = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
ScrollView* scrollView = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
LinearLayout* innerView = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
if (listIP.size() > 0) {
for (const auto& label : listIP) {
// Filter out IP prefixed with "127." and "169.254." also "0." since they can be rendundant or unusable
if (label.find("127.") != 0 && label.find("169.254.") != 0 && label.find("0.") != 0) {
auto button = innerView->Add(new Button(label, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
button->OnClick.Handle(this, &HostnameSelectScreen::OnIPClick);
button->SetTag(label);
}
}
}
scrollView->Add(innerView);
ipRows_->Add(scrollView);
ipRows_->SetVisibility(V_GONE);
parent->Add(ipRows_);
listIP.clear(); listIP.shrink_to_fit();

errorView_ = parent->Add(new TextView(n->T("Invalid IP or hostname"), ALIGN_HCENTER, false, new LinearLayoutParams(Margins(0, 10, 0, 0))));
errorView_->SetTextColor(0xFF3030FF);
errorView_->SetVisibility(V_GONE);
Expand Down Expand Up @@ -1741,6 +1758,38 @@ UI::EventReturn HostnameSelectScreen::OnDeleteAllClick(UI::EventParams &e) {
return UI::EVENT_DONE;
}

UI::EventReturn HostnameSelectScreen::OnEditClick(UI::EventParams& e) {
auto n = GetI18NCategory("Networking");
#if defined(__ANDROID__)
System_InputBoxGetString(n->T("proAdhocServer Address:"), addrView_->GetText(), [this](bool result, const std::string& value) {
if (result) {
addrView_->SetText(value);
}
});
#endif
return UI::EVENT_DONE;
}

UI::EventReturn HostnameSelectScreen::OnShowIPListClick(UI::EventParams& e) {
if (ipRows_->GetVisibility() == UI::V_GONE) {
ipRows_->SetVisibility(UI::V_VISIBLE);
}
else {
ipRows_->SetVisibility(UI::V_GONE);
}
return UI::EVENT_DONE;
}

UI::EventReturn HostnameSelectScreen::OnIPClick(UI::EventParams& e) {
std::string text = e.v ? e.v->Tag() : "";
if (text.length() > 0) {
addrView_->SetText(text);
// TODO: Copy the IP to clipboard for the host to easily share their IP through chatting apps.
System_SendMessage("setclipboardtext", text.c_str()); // Doesn't seems to be working on windows (yet?)
}
return UI::EVENT_DONE;
}

void HostnameSelectScreen::ResolverThread() {
std::unique_lock<std::mutex> guard(resolverLock_);

Expand Down
4 changes: 4 additions & 0 deletions UI/GameSettingsScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ class HostnameSelectScreen : public PopupScreen {
UI::EventReturn OnPointClick(UI::EventParams &e);
UI::EventReturn OnDeleteClick(UI::EventParams &e);
UI::EventReturn OnDeleteAllClick(UI::EventParams &e);
UI::EventReturn OnEditClick(UI::EventParams& e);
UI::EventReturn OnShowIPListClick(UI::EventParams& e);
UI::EventReturn OnIPClick(UI::EventParams& e);

enum class ResolverState {
WAITING,
Expand All @@ -221,6 +224,7 @@ class HostnameSelectScreen : public PopupScreen {
UI::TextEdit *addrView_ = nullptr;
UI::TextView *errorView_ = nullptr;
UI::TextView *progressView_ = nullptr;
UI::LinearLayout *ipRows_ = nullptr;

std::thread resolver_;
ResolverState resolverState_ = ResolverState::WAITING;
Expand Down
Loading