#include <algorithm>
#include "ppsspp_config.h"
#undef new
#include "ext/rapidjson/include/rapidjson/document.h"
#include "Common/DbgNew.h"
#include "AdhocServerScreen.h"
#include "Core/Util/GameDB.h"
#include "Common/Net/Resolve.h"
#include "Common/UI/Root.h"
#include "Common/UI/PopupScreens.h"
#include "Common/StringUtils.h"
#include "Common/Net/HTTPClient.h"
#include "Core/HLE/sceNetAdhoc.h"
#include "UI/MiscViews.h"
static void UpgradeGameName(std::string *str) {
if (str->size() == 9) {
std::vector<GameDBInfo> infos;
if (g_gameDB.GetGameInfos(*str, &infos)) {
*str = infos[0].title;
}
}
}
std::vector<AdhocGame> ParseDataJson(std::string_view json) {
rapidjson::Document d;
d.Parse(json.data(), json.size());
std::vector<AdhocGame> gameList;
if (d.HasParseError() || !d.HasMember("games")) return gameList;
const auto& gamesArray = d["games"];
for (auto& g : gamesArray.GetArray()) {
AdhocGame game;
game.name = g["name"].GetString();
UpgradeGameName(&game.name);
game.usercount = std::stoi(g["usercount"].GetString());
if (g.HasMember("groups")) {
for (auto& grp : g["groups"].GetArray()) {
AdhocGroup group;
group.name = grp["name"].GetString();
group.usercount = std::stoi(grp["usercount"].GetString());
if (grp.HasMember("users")) {
for (auto& u : grp["users"].GetArray()) {
AdhocUser user;
user.name = u["name"].GetString();
for (auto& p : u["pdp_ports"].GetArray())
user.pdp_ports.push_back(p.GetInt());
for (auto& p : u["ptp_ports"].GetArray())
user.ptp_ports.push_back(p.GetInt());
group.users.push_back(user);
}
}
game.groups.push_back(group);
}
}
gameList.push_back(game);
}
return gameList;
}
class AdhocAddServerPopupScreen : public UI::PopupScreen {
public:
AdhocAddServerPopupScreen(std::string *outEditValue) : PopupScreen(T(I18NCat::NETWORKING, "Add server"), T(I18NCat::DIALOG, "Add"), T(I18NCat::DIALOG, "Cancel")), outEditValue_(outEditValue) {
}
void CreatePopupContents(UI::ViewGroup *parent) override {
using namespace UI;
auto ni = GetI18NCategory(I18NCat::NETWORKING);
PopupTextInputChoice *textInputChoice = parent->Add(new PopupTextInputChoice(GetRequesterToken(), &editValue_, ni->T("Hostname or IP"), "", 450, screenManager()));
textInputChoice->SetShadowText(ni->T("Hostname or IP"));
parent->Add(new CheckBox(&hasRelay_, ni->T("Relay server mode")));
}
virtual void OnCompleted(DialogResult result) override {
if (result == DialogResult::DR_OK) {
std::vector<AdhocServerListEntry> servers = AdhocGetServerList(AdhocLoadListMode::CacheOnlySync);
bool preset = false;
for (auto &iter : servers) {
if (equalsNoCase(editValue_, iter.host)) {
preset = true;
}
}
if (!preset) {
if (hasRelay_) {
if (!ContainsNoCase(g_Config.vCustomAdhocServerListWithRelay, editValue_) &&
!ContainsNoCase(g_Config.vCustomAdhocServerList, editValue_)) {
g_Config.vCustomAdhocServerListWithRelay.insert(g_Config.vCustomAdhocServerListWithRelay.begin(), editValue_);
}
} else {
if (!ContainsNoCase(g_Config.vCustomAdhocServerList, editValue_) &&
!ContainsNoCase(g_Config.vCustomAdhocServerListWithRelay, editValue_)) {
g_Config.vCustomAdhocServerList.insert(g_Config.vCustomAdhocServerList.begin(), editValue_);
}
}
}
*outEditValue_ = editValue_;
}
}
virtual bool CanComplete(DialogResult result) override { return result == DR_OK ? !editValue_.empty() : true; }
const char *tag() const override { return "AdhocAddServerPopup"; }
private:
std::string editValue_;
std::string *outEditValue_;
bool hasRelay_ = false;
};
class AdhocServerCompactInfo : public UI::LinearLayout {
public:
AdhocServerCompactInfo(const AdhocServerListEntry &entry, UI::LayoutParams *layoutParams = nullptr);
void Draw(UIContext &dc) override {
UI::LinearLayout::Draw(dc);
dc.Draw()->DrawImageCenterTexel(dc.GetTheme().whiteImage, bounds_.x, bounds_.y2() - 2, bounds_.x2(), bounds_.y2(), dc.GetTheme().popupTitleStyle.fgColor);
}
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override {
w = 500; h = 100;
}
private:
AdhocServerListEntry entry_;
};
AdhocServerCompactInfo::AdhocServerCompactInfo(const AdhocServerListEntry &entry, UI::LayoutParams *layoutParams)
: UI::LinearLayout(ORIENT_HORIZONTAL, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT, UI::Margins(5.0f, 0.0f))), entry_(entry) {
using namespace UI;
SetSpacing(5.0f);
LinearLayout *lines = Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(Margins(5, 5))));
lines->SetSpacing(0.0f);
TextView *name = lines->Add(new TextView(entry.name));
std::string secondLine = entry.host;
auto n = GetI18NCategory(I18NCat::NETWORKING);
if (!entry.location.empty()) {
secondLine += ": " + entry.location;
}
lines->Add(new TextView(secondLine))->SetTextSize(TextSize::Small)->SetWordWrap();
Add(new Spacer(0.0f, new LinearLayoutParams(1.0f, Margins(0.0f, 5.0f))));
if (entry.mode == AdhocDataMode::AemuPostoffice) {
TextView *relay = Add(new TextView(n->T("Relay"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity::G_VCENTER, Margins(10.0))));
}
Add(new Choice(ImageID("I_FILE_COPY"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)))->OnClick.Add([host = entry_.host](UI::EventParams &) {
System_CopyStringToClipboard(host);
});
}
static UI::View *CreateInfoItemWithButton(std::string_view text, ImageID buttonImage, std::function<void(UI::EventParams &)> onClick) {
using namespace UI;
LinearLayout *line = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, Margins(12, 0)));
line->Add(new TextView(text, new LinearLayoutParams(0.0f, Gravity::G_VCENTER)));
line->Add(new Spacer(0, new LinearLayoutParams(1.0f)));
line->Add(new Choice(buttonImage, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT)))->OnClick.Add(onClick);
return line;
}
static UI::View *CreateLinkButton(std::string url, std::string_view title = "") {
using namespace UI;
auto cr = GetI18NCategory(I18NCat::PSPCREDITS);
auto st = GetI18NCategory(I18NCat::STORE);
ImageID icon = ImageID("I_LINK_OUT_QUESTION");
if (startsWith(url, "https://discord")) {
icon = ImageID("I_LOGO_DISCORD");
if (title.empty())
title = cr->T("Discord");
} else {
icon = ImageID("I_LINK_OUT");
if (title.empty())
title = st->T("Website");
}
Choice *choice = new Choice(title, icon, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT));
choice->OnClick.Add([url](UI::EventParams &) {
System_LaunchUrl(LaunchUrlType::BROWSER_URL, url);
});
return choice;
}
AdhocServerInfoScreen::AdhocServerInfoScreen(const AdhocServerListEntry &entry)
: UI::PopupScreen("", T(I18NCat::DIALOG, "Back")), entry_(entry) {
if (!entry.dataJsonUrl.empty()) {
statusRequest_ = g_DownloadManager.StartDownload(entry.dataJsonUrl, Path(), http::RequestFlags::KeepInMemory, nullptr, "status");
}
}
void AdhocServerInfoScreen::CreatePopupContents(UI::ViewGroup *parent) {
using namespace UI;
auto pa = GetI18NCategory(I18NCat::PAUSE);
auto di = GetI18NCategory(I18NCat::DIALOG);
auto ni = GetI18NCategory(I18NCat::NETWORKING);
Margins contentMargins(12, 0);
parent->Add(new AdhocServerCompactInfo(entry_, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, contentMargins)));
parent->Add(new Spacer(5.0f));
ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f));
LinearLayout *content = new LinearLayout(ORIENT_VERTICAL);
content->SetSpacing(6.0f);
if (!entry_.ip.empty()) {
content->Add(new InfoItem(entry_.ip, ""));
}
TextView *desc = content->Add(new TextView(entry_.description, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, contentMargins)));
desc->SetTextSize(TextSize::Small);
desc->SetWordWrap();
LinearLayout *buttonStrip = content->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, contentMargins)));
buttonStrip->SetSpacing(8);
if (!entry_.web.empty() || !entry_.discord.empty()) {
if (!entry_.web.empty()) {
buttonStrip->Add(CreateLinkButton(entry_.web));
}
if (!entry_.discord.empty()) {
buttonStrip->Add(CreateLinkButton(entry_.discord));
}
}
if (entry_.dataJsonUrl.empty()) {
content->Add(CreateInfoItemWithButton(ni->T("This server has no data.json status page"), ImageID("I_LINK_OUT_QUESTION"), [](UI::EventParams &e) {
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.ppsspp.org/docs/multiplayer/adhoc-server-status/");
}));
if (!entry_.statusWebUrl.empty()) {
buttonStrip->Add(CreateLinkButton(entry_.statusWebUrl, ni->T("Status")));
}
if (!entry_.statusXmlUrl.empty()) {
buttonStrip->Add(CreateLinkButton(entry_.statusXmlUrl, ni->T("Status")));
}
} else {
if (games_.empty()) {
if (statusRequest_) {
} else {
content->Add(new TextView(ni->T("No games in progress on this server")));
}
} else {
for (const AdhocGame &game : games_) {
std::string title = game.name + " - " + ApplySafeSubstitutions(ni->T("players: %1"), game.usercount) + " " + ApplySafeSubstitutions(ni->T("groups: %1"), (int)game.groups.size());
CollapsibleSection *gameSection = content->Add(new CollapsibleSection(title));
for (const AdhocGroup &group : game.groups) {
if (group.usercount == 1 && group.name == "Groupless") {
gameSection->Add(new TextView(" " + ApplySafeSubstitutions(ni->T("Players waiting: %1"), group.usercount)))->SetTextSize(TextSize::Small);
continue;
}
gameSection->Add(new TextView(" " + group.name + " - " + ApplySafeSubstitutions(ni->T("players: %1"), group.usercount)))->SetTextSize(TextSize::Small);
for (const AdhocUser &user : group.users) {
std::string portInfo;
if (!user.pdp_ports.empty()) {
portInfo += "PDP: ";
for (int port : user.pdp_ports) {
portInfo += std::to_string(port) + " ";
}
}
if (!user.ptp_ports.empty()) {
portInfo += "PTP: ";
for (int port : user.ptp_ports) {
portInfo += std::to_string(port) + " ";
}
}
gameSection->Add(new TextView(" " + user.name + " " + portInfo))->SetTextSize(TextSize::Tiny);
}
}
gameSection->SetOpen(false);
}
}
}
scroll->Add(content);
parent->Add(scroll);
}
void AdhocServerInfoScreen::update() {
UI::PopupScreen::update();
if (statusRequest_ && statusRequest_->Done()) {
std::string json;
statusRequest_->buffer().TakeAll(&json);
games_ = ParseDataJson(json);
statusRequest_.reset();
RecreateViews();
}
}
void AddDeleteButton(std::string *editValue, ScreenManager *screenManager, UI::ViewGroup *viewGroup, const AdhocServerListEntry &entry) {
using namespace UI;
Choice *deleteButton = viewGroup->Add(new Choice(ImageID("I_TRASHCAN"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity::G_VCENTER, Margins(0, 0, 10, 0))));
deleteButton->OnClick.Add([host = entry.host, screenManager, editValue](UI::EventParams &e) {
auto di = GetI18NCategory(I18NCat::DIALOG);
const std::string quotedHost = "\"" + host + "\"";
const std::string message = ApplySafeSubstitutions(di->T("Are you sure you want to delete %1?"), quotedHost);
screenManager->push(new UI::MessagePopupScreen(di->T("Delete"), message, di->T("Delete"), di->T("Cancel"), [host, editValue](bool confirmed) {
if (confirmed) {
RemoveNoCase(g_Config.vCustomAdhocServerList, host);
RemoveNoCase(g_Config.vCustomAdhocServerListWithRelay, host);
if (*editValue == host) {
*editValue = DefaultProAdhocServer();
}
}
}));
});
}
class AdhocServerRow : public UI::LinearLayout {
public:
AdhocServerRow(std::string *value, const AdhocServerListEntry &entry, bool showDeleteButton, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr);
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override {
w = 500; h = 90;
}
void Draw(UIContext &dc) override {
dc.FillRect(dc.GetTheme().itemStyle.background, bounds_);
if (*value_ == entry_.host) {
dc.FillRect(UI::Drawable(0x48FFFFFF), GetBounds());
}
LinearLayout::Draw(dc);
}
bool Touch(const TouchInput &input) override {
using namespace UI;
if (UI::LinearLayout::Touch(input)) {
return true;
}
if (input.flags & TouchInputFlags::DOWN) {
if (bounds_.Contains(input.x, input.y)) {
dragging_ = true;
return true;
}
}
if (dragging_ && (input.flags & TouchInputFlags::UP)) {
dragging_ = false;
if (!(input.flags & TouchInputFlags::CANCEL) && bounds_.Contains(input.x, input.y)) {
EventParams e;
e.v = this;
OnSelected.Trigger(e);
return true;
}
}
return false;
}
UI::Event OnSelected;
private:
bool dragging_ = false;
std::string *value_;
AdhocServerListEntry entry_;
};
AdhocServerRow::AdhocServerRow(std::string *editValue, const AdhocServerListEntry &entry, bool showDeleteButton, ScreenManager *screenManager, UI::LayoutParams *layoutParams)
: UI::LinearLayout(ORIENT_HORIZONTAL, new UI::LinearLayoutParams(UI::FILL_PARENT, UI::WRAP_CONTENT, UI::Margins(5.0f, 0.0f))), value_(editValue), entry_(entry) {
using namespace UI;
SetSpacing(5.0f);
int number = 0;
Add(new ImageView([editValue, host = entry.host]() { return host == *editValue ? ImageID("I_RADIO_SELECTED") : ImageID("I_RADIO_EMPTY"); },
new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity::G_VCENTER, Margins(5, 0, 0, 0))));
LinearLayout *lines = Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(Margins(5, 5))));
lines->SetSpacing(0.0f);
ClickableTextView *name = lines->Add(new ClickableTextView(entry.name));
name->SetFocusable(true);
name->OnClick.Add([this](UI::EventParams &e) {
EventParams e2;
e2.v = this;
OnSelected.Trigger(e2);
});
std::string secondLine = entry.host;
auto n = GetI18NCategory(I18NCat::NETWORKING);
if (entry.host == "localhost") {
secondLine = n->T("Ad hoc server address hint");
}
if (!entry.location.empty()) {
secondLine += ": " + entry.location;
}
lines->Add(new TextView(secondLine))->SetTextSize(TextSize::Small)->SetWordWrap();
Add(new Spacer(0.0f, new LinearLayoutParams(1.0f, Margins(0.0f, 5.0f))));
if (entry.mode == AdhocDataMode::AemuPostoffice) {
TextView *relay = Add(new TextView(n->T("Relay"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity::G_VCENTER, Margins(10.0))));
}
if (showDeleteButton) {
AddDeleteButton(editValue, screenManager, this, entry);
}
if (!entry.description.empty()) {
Choice *infoButton = Add(new Choice(ImageID("I_INFO"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, Gravity::G_VCENTER, Margins(0, 0, 10, 0))));
AdhocServerListEntry copy = entry;
infoButton->OnClick.Add([this, copy, screenManager](UI::EventParams &e) {
e.v = this;
screenManager->push(new AdhocServerInfoScreen(copy));
});
}
}
AdhocServerScreen::AdhocServerScreen(std::string *value, std::string_view title)
: UI::PopupScreen(title, T(I18NCat::DIALOG, "OK"), T(I18NCat::DIALOG, "Cancel")), value_(value) {
resolver_ = std::thread([](AdhocServerScreen *thiz) {
thiz->ResolverThread();
}, this);
editValue_ = *value;
AdhocLoadServerList(AdhocLoadListMode::AllSourcesAsync);
}
AdhocServerScreen::~AdhocServerScreen() {
{
std::unique_lock<std::mutex> guard(resolverLock_);
resolverState_ = ResolverState::QUIT;
resolverCond_.notify_one();
}
resolver_.join();
}
void AdhocServerScreen::sendMessage(UIMessage message, const char *value) {
if (message == UIMessage::ADHOC_SERVER_LIST_CHANGED) {
RecreateViews();
}
}
void AdhocServerScreen::CreatePopupContents(UI::ViewGroup *parent) {
using namespace UI;
auto sy = GetI18NCategory(I18NCat::SYSTEM);
auto di = GetI18NCategory(I18NCat::DIALOG);
auto n = GetI18NCategory(I18NCat::NETWORKING);
std::vector<AdhocServerListEntry> listItems = AdhocGetServerList(AdhocLoadListMode::CacheOnlySync);
Choice *addServer = parent->Add(new Choice(n->T("Add server"), ImageID("I_PLUS")));
addServer->OnClick.Add([this](UI::EventParams &e) {
AdhocAddServerPopupScreen *addScreen = new AdhocAddServerPopupScreen(&editValue_);
screenManager()->push(addScreen);
});
parent->Add(new Spacer(5.0f));
std::vector<AdhocServerListEntry> entries = listItems;
std::vector<AdhocServerListEntry> localEntries;
std::vector<AdhocServerListEntry> customEntries;
bool currentServerFound = false;
for (const auto &iter : entries) {
if (iter.host == editValue_) {
currentServerFound = true;
}
}
{
AdhocServerListEntry localhostEntry;
localhostEntry.name = "localhost";
localhostEntry.host = "localhost";
localEntries.push_back(localhostEntry);
std::vector<std::string> listIP;
net::GetLocalIP4List(listIP);
for (const auto &ipAddress : listIP) {
if (startsWith(ipAddress, "127.") || startsWith(ipAddress, "169.254.") || startsWith(ipAddress, "0.")) {
continue;
}
AdhocServerListEntry entry;
entry.name = ipAddress;
entry.host = ipAddress;
localEntries.push_back(entry);
if (ipAddress == editValue_) {
currentServerFound = true;
}
}
}
auto hostInEntries = [entries](const std::string &host) {
for (const auto &entry : entries) {
if (entry.host == host) {
return true;
}
}
return false;
};
for (auto iter = g_Config.vCustomAdhocServerListWithRelay.begin(); iter != g_Config.vCustomAdhocServerListWithRelay.end();) {
if (hostInEntries(*iter) || iter->empty()) {
iter = g_Config.vCustomAdhocServerListWithRelay.erase(iter);
recreateParent_ = true;
continue;
}
AdhocServerListEntry entry;
entry.name = *iter;
entry.host = *iter;
entry.mode = AdhocDataMode::AemuPostoffice;
customEntries.push_back(entry);
if (*iter == editValue_) {
currentServerFound = true;
}
iter++;
}
for (auto iter = g_Config.vCustomAdhocServerList.begin(); iter != g_Config.vCustomAdhocServerList.end();) {
if (hostInEntries(*iter) || iter->empty()) {
iter = g_Config.vCustomAdhocServerList.erase(iter);
recreateParent_ = true;
continue;
}
AdhocServerListEntry entry;
entry.name = *iter;
entry.host = *iter;
entry.mode = AdhocDataMode::P2P;
customEntries.push_back(entry);
if (*iter == editValue_) {
currentServerFound = true;
}
iter++;
}
ScrollView *scrollView = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));
LinearLayout *innerView = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
innerView->SetSpacing(5.0f);
auto AddButtonFromEntry = [this](UI::ViewGroup *parent, const AdhocServerListEntry &entry, bool showDeleteButton) {
AdhocServerRow *row = new AdhocServerRow(&editValue_, entry, showDeleteButton, screenManager());
parent->Add(row);
row->OnSelected.Add([this](UI::EventParams &e) {
std::string value = e.v->Tag();
if (!value.empty()) {
editValue_ = value;
}
});
row->SetTag(entry.host);
};
if (!customEntries.empty()) {
CollapsibleSection *customSection = innerView->Add(new CollapsibleSection(n->T("Custom server list"), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
if (!currentServerFound) {
AdhocServerListEntry entry;
entry.name = editValue_;
entry.host = editValue_;
entry.mode = (AdhocServerRelayMode)g_Config.iAdhocServerRelayMode == AdhocServerRelayMode::AlwaysOn ? AdhocDataMode::AemuPostoffice : AdhocDataMode::P2P;
if (entry.mode == AdhocDataMode::AemuPostoffice) {
g_Config.vCustomAdhocServerListWithRelay.insert(g_Config.vCustomAdhocServerListWithRelay.begin(), editValue_);
} else {
g_Config.vCustomAdhocServerList.insert(g_Config.vCustomAdhocServerList.begin(), editValue_);
}
recreateParent_ = true;
AddButtonFromEntry(customSection, entry, true);
}
for (const auto &entry : customEntries) {
AddButtonFromEntry(customSection, entry, true);
}
}
CollapsibleSection *publicSection = innerView->Add(new CollapsibleSection(n->T("Public server list"), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
for (const auto &entry : entries) {
AddButtonFromEntry(publicSection, entry, false);
}
CollapsibleSection *localSection = innerView->Add(new CollapsibleSection(n->T("Local network addresses"), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
for (const auto &entry : localEntries) {
AddButtonFromEntry(localSection, entry, false);
}
scrollView->Add(innerView);
parent->Add(scrollView);
progressView_ = parent->Add(new NoticeView(NoticeLevel::INFO, n->T("Validating address..."), "", new LinearLayoutParams(Margins(0, 5, 0, 0))));
progressView_->SetVisibility(UI::V_GONE);
}
void AdhocServerScreen::ResolverThread() {
std::unique_lock<std::mutex> guard(resolverLock_);
while (resolverState_ != ResolverState::QUIT) {
resolverCond_.wait(guard);
if (resolverState_ == ResolverState::QUEUED) {
resolverState_ = ResolverState::PROGRESS;
addrinfo *resolved = nullptr;
std::string err;
toResolveResult_ = net::DNSResolve(toResolve_, "80", &resolved, err);
if (resolved)
net::DNSResolveFree(resolved);
resolverState_ = ResolverState::READY;
}
}
}
bool AdhocServerScreen::CanComplete(DialogResult result) {
auto n = GetI18NCategory(I18NCat::NETWORKING);
if (result != DR_OK)
return true;
std::string value = editValue_;
if (lastResolved_ == value) {
return true;
}
if (resolverState_ == ResolverState::PROGRESS)
return false;
std::lock_guard<std::mutex> guard(resolverLock_);
switch (resolverState_) {
case ResolverState::PROGRESS:
case ResolverState::QUIT:
return false;
case ResolverState::QUEUED:
case ResolverState::WAITING:
break;
case ResolverState::READY:
if (toResolve_ == value) {
resolverState_ = ResolverState::WAITING;
toResolve_.clear();
lastResolved_ = value;
lastResolvedResult_ = toResolveResult_;
if (lastResolvedResult_) {
progressView_->SetVisibility(UI::V_GONE);
} else {
progressView_->SetText(n->T("Invalid IP or hostname"));
progressView_->SetLevel(NoticeLevel::ERROR);
progressView_->SetVisibility(UI::V_VISIBLE);
}
return true;
}
break;
}
resolverState_ = ResolverState::QUEUED;
toResolve_ = value;
resolverCond_.notify_one();
progressView_->SetText(n->T("Validating address..."));
progressView_->SetLevel(NoticeLevel::INFO);
progressView_->SetVisibility(UI::V_VISIBLE);
return false;
}
void AdhocServerScreen::OnCompleted(DialogResult result) {
if (result == DR_OK) {
*value_ = StripSpaces(editValue_);
}
}
bool AdhocServerNameIsCustom() {
for (auto iter : g_Config.vCustomAdhocServerList) {
if (iter == g_Config.sProAdhocServer) {
return true;
}
}
for (auto iter : g_Config.vCustomAdhocServerListWithRelay) {
if (iter == g_Config.sProAdhocServer) {
return true;
}
}
return false;
}
static void EditServerName(std::string_view newServerName) {
newServerName = StripSpaces(newServerName);
if (newServerName != g_Config.sProAdhocServer) {
for (auto &name : g_Config.vCustomAdhocServerList) {
if (name == g_Config.sProAdhocServer) {
name = newServerName;
}
}
for (auto &name : g_Config.vCustomAdhocServerListWithRelay) {
if (name == g_Config.sProAdhocServer) {
name = newServerName;
}
}
g_Config.sProAdhocServer = newServerName;
}
}
void AskToEditCurrentServer(int requestToken, ScreenManager *screenManager) {
using namespace UI;
auto n = GetI18NCategory(I18NCat::NETWORKING);
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) {
System_InputBoxGetString(requestToken, n->T("Ad hoc server address"), g_Config.sProAdhocServer, false, [](const std::string &enteredValue, int) {
EditServerName(enteredValue);
});
return;
}
static std::string editText = g_Config.sProAdhocServer;
TextEditPopupScreen *popupScreen = new TextEditPopupScreen(&editText, editText, n->T("Ad hoc server address"), 256);
if (System_GetPropertyBool(SYSPROP_KEYBOARD_IS_SOFT)) {
popupScreen->SetAlignTop(true);
}
popupScreen->OnChange.Add([](EventParams &e) {
EditServerName(editText);
});
screenManager->push(popupScreen);
}