Merge pull request #12510 from sethalves/serverless-domains

Serverless domains
This commit is contained in:
John Conklin II 2018-03-22 14:06:49 -07:00 committed by GitHub
commit 6d9892856d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
33 changed files with 484 additions and 228 deletions

View file

@ -377,8 +377,6 @@ static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop";
static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system";
static const QString DOMAIN_SPAWNING_POINT = "/0, -10, 0";
const std::vector<std::pair<QString, Application::AcceptURLMethod>> Application::_acceptedExtensions {
{ SVO_EXTENSION, &Application::importSVOFromURL },
{ SVO_JSON_EXTENSION, &Application::importSVOFromURL },
@ -513,6 +511,27 @@ std::atomic<uint64_t> DeadlockWatchdogThread::_maxElapsed;
std::atomic<int> DeadlockWatchdogThread::_maxElapsedAverage;
ThreadSafeMovingAverage<int, DeadlockWatchdogThread::HEARTBEAT_SAMPLES> DeadlockWatchdogThread::_movingAverage;
bool isDomainURL(QUrl url) {
if (!url.isValid()) {
return false;
}
if (url.scheme() == URL_SCHEME_HIFI) {
return true;
}
if (url.scheme() != URL_SCHEME_FILE) {
// TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can
// be loaded over http(s)
// && url.scheme() != URL_SCHEME_HTTP &&
// url.scheme() != URL_SCHEME_HTTPS
return false;
}
if (url.path().endsWith(".json", Qt::CaseInsensitive) ||
url.path().endsWith(".json.gz", Qt::CaseInsensitive)) {
return true;
}
return false;
}
#ifdef Q_OS_WIN
class MyNativeEventFilter : public QAbstractNativeEventFilter {
public:
@ -542,7 +561,7 @@ public:
if (message->message == WM_COPYDATA) {
COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)(message->lParam);
QUrl url = QUrl((const char*)(pcds->lpData));
if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) {
if (isDomainURL(url)) {
DependencyManager::get<AddressManager>()->handleLookupString(url.toString());
return true;
}
@ -940,6 +959,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
setProperty(hifi::properties::CRASHED, _previousSessionCrashed);
_entityClipboard->setIsServerlessMode(true);
{
const QString TEST_SCRIPT = "--testScript";
const QString TRACE_FILE = "--traceFile";
@ -1035,7 +1056,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// setup a timer for domain-server check ins
QTimer* domainCheckInTimer = new QTimer(this);
connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);
connect(domainCheckInTimer, &QTimer::timeout, [this, nodeList] {
if (!isServerlessMode()) {
nodeList->sendDomainServerCheckIn();
}
});
domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);
connect(this, &QCoreApplication::aboutToQuit, [domainCheckInTimer] {
domainCheckInTimer->stop();
@ -1097,9 +1122,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainURLChanged(QUrl)));
connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));
connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle()));
connect(&domainHandler, SIGNAL(connectedToDomain(QUrl)), SLOT(updateWindowTitle()));
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &Application::clearDomainAvatars);
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this]() {
@ -2046,7 +2071,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(&_addAssetToWorldErrorTimer, &QTimer::timeout, this, &Application::addAssetToWorldErrorTimeout);
connect(this, &QCoreApplication::aboutToQuit, this, &Application::addAssetToWorldMessageClose);
connect(&domainHandler, &DomainHandler::hostnameChanged, this, &Application::addAssetToWorldMessageClose);
connect(&domainHandler, &DomainHandler::domainURLChanged, this, &Application::addAssetToWorldMessageClose);
updateSystemTabletMode();
@ -3023,27 +3048,27 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
QString sentTo;
// If this is a first run we short-circuit the address passed in
if (firstRun.get()) {
// If this is a first run we short-circuit the address passed in
if (firstRun.get()) {
#if !defined(Q_OS_ANDROID)
showHelp();
#endif
if (sandboxIsRunning) {
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
DependencyManager::get<AddressManager>()->goToLocalSandbox();
sentTo = SENT_TO_SANDBOX;
} else {
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
}
firstRun.set(false);
showHelp();
#endif
if (sandboxIsRunning) {
qCDebug(interfaceapp) << "Home sandbox appears to be running, going to Home.";
DependencyManager::get<AddressManager>()->goToLocalSandbox();
sentTo = SENT_TO_SANDBOX;
} else {
qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
sentTo = SENT_TO_PREVIOUS_LOCATION;
qCDebug(interfaceapp) << "Home sandbox does not appear to be running, going to Entry.";
DependencyManager::get<AddressManager>()->goToEntry();
sentTo = SENT_TO_ENTRY;
}
firstRun.set(false);
} else {
qCDebug(interfaceapp) << "Not first run... going to" << qPrintable(addressLookupString.isEmpty() ? QString("previous location") : addressLookupString);
DependencyManager::get<AddressManager>()->loadSettings(addressLookupString);
sentTo = SENT_TO_PREVIOUS_LOCATION;
}
UserActivityLogger::getInstance().logAction("startup_sent_to", {
{ "sent_to", sentTo },
@ -3083,6 +3108,57 @@ bool Application::importFromZIP(const QString& filePath) {
return true;
}
bool Application::isServerlessMode() const {
auto tree = getEntities()->getTree();
if (tree) {
return tree->isServerlessMode();
}
return false;
}
void Application::setIsServerlessMode(bool serverlessDomain) {
auto tree = getEntities()->getTree();
if (tree) {
tree->setIsServerlessMode(serverlessDomain);
}
}
void Application::loadServerlessDomain(QUrl domainURL) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "loadServerlessDomain", Q_ARG(QUrl, domainURL));
return;
}
if (domainURL.isEmpty()) {
return;
}
QUuid serverlessSessionID = QUuid::createUuid();
getMyAvatar()->setSessionUUID(serverlessSessionID);
auto nodeList = DependencyManager::get<NodeList>();
nodeList->setSessionUUID(serverlessSessionID);
// there is no domain-server to tell us our permissions, so enable all
NodePermissions permissions;
permissions.setAll(true);
nodeList->setPermissions(permissions);
// we can't import directly into the main tree because we would need to lock it, and
// Octree::readFromURL calls loop.exec which can run code which will also attempt to lock the tree.
EntityTreePointer tmpTree(new EntityTree());
tmpTree->setIsServerlessMode(true);
tmpTree->createRootElement();
auto myAvatar = getMyAvatar();
tmpTree->setMyAvatar(myAvatar);
bool success = tmpTree->readFromURL(domainURL.toString());
if (success) {
tmpTree->reaverageOctreeElements();
tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), 0, 0, 0);
}
_fullSceneReceivedCounter++;
}
bool Application::importImage(const QString& urlString) {
qCDebug(interfaceapp) << "An image file has been dropped in";
QString filepath(urlString);
@ -4583,7 +4659,7 @@ void Application::initDisplay() {
}
void Application::init() {
// Make sure Login state is up to date
DependencyManager::get<DialogsManager>()->toggleLoginDialog();
if (!DISABLE_DEFERRED) {
@ -4608,7 +4684,9 @@ void Application::init() {
qCDebug(interfaceapp) << "Loaded settings";
// fire off an immediate domain-server check in now that settings are loaded
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
if (!isServerlessMode()) {
DependencyManager::get<NodeList>()->sendDomainServerCheckIn();
}
// This allows collision to be set up properly for shape entities supported by GeometryCache.
// This is before entity setup to ensure that it's ready for whenever instance collision is initialized.
@ -5742,10 +5820,15 @@ void Application::updateWindowTitle() const {
QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
QString username = accountManager->getAccountInfo().getUsername();
QString currentPlaceName = DependencyManager::get<AddressManager>()->getHost();
if (currentPlaceName.isEmpty()) {
currentPlaceName = nodeList->getDomainHandler().getHostname();
QString currentPlaceName;
if (isServerlessMode()) {
currentPlaceName = "serverless: " + DependencyManager::get<AddressManager>()->getDomainURL().toString();
} else {
currentPlaceName = DependencyManager::get<AddressManager>()->getDomainURL().host();
if (currentPlaceName.isEmpty()) {
currentPlaceName = nodeList->getDomainHandler().getHostname();
}
}
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
@ -5758,7 +5841,7 @@ void Application::updateWindowTitle() const {
_window->setWindowTitle(title);
// updateTitleWindow gets called whenever there's a change regarding the domain, so rather
// than placing this within domainChanged, it's placed here to cover the other potential cases.
// than placing this within domainURLChanged, it's placed here to cover the other potential cases.
DependencyManager::get< MessagesClient >()->sendLocalMessage("Toolbar-DomainChanged", "");
}
@ -5797,15 +5880,22 @@ void Application::clearDomainAvatars() {
DependencyManager::get<AvatarManager>()->clearOtherAvatars();
}
void Application::domainChanged(const QString& domainHostname) {
updateWindowTitle();
void Application::domainURLChanged(QUrl domainURL) {
// disable physics until we have enough information about our new location to not cause craziness.
resetPhysicsReadyInformation();
setIsServerlessMode(domainURL.scheme() != URL_SCHEME_HIFI);
if (isServerlessMode()) {
loadServerlessDomain(domainURL);
}
updateWindowTitle();
}
void Application::resettingDomain() {
_notifiedPacketVersionMismatchThisDomain = false;
auto nodeList = DependencyManager::get<NodeList>();
clearDomainOctreeDetails();
}
void Application::nodeAdded(SharedNodePointer node) const {
@ -5922,22 +6012,22 @@ bool Application::nearbyEntitiesAreReadyForPhysics() {
AABox avatarBox(getMyAvatar()->getWorldPosition() - glm::vec3(PHYSICS_READY_RANGE), glm::vec3(2 * PHYSICS_READY_RANGE));
// create two functions that use avatarBox (entityScan and elementScan), the second calls the first
std::function<bool (EntityItemPointer&)> entityScan = [=](EntityItemPointer& entity) {
if (entity->shouldBePhysical()) {
bool success = false;
AABox entityBox = entity->getAABox(success);
// important: bail for entities that cannot supply a valid AABox
return success && avatarBox.touches(entityBox);
}
return false;
};
if (entity->shouldBePhysical()) {
bool success = false;
AABox entityBox = entity->getAABox(success);
// important: bail for entities that cannot supply a valid AABox
return success && avatarBox.touches(entityBox);
}
return false;
};
std::function<bool(const OctreeElementPointer&, void*)> elementScan = [&](const OctreeElementPointer& element, void* unused) {
if (element->getAACube().touches(avatarBox)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->getEntities(entityScan, entities);
return true;
}
return false;
};
if (element->getAACube().touches(avatarBox)) {
EntityTreeElementPointer entityTreeElement = std::static_pointer_cast<EntityTreeElement>(element);
entityTreeElement->getEntities(entityScan, entities);
return true;
}
return false;
};
entityTree->withReadLock([&] {
// Pass the second function to the general-purpose EntityTree::findEntities()
@ -6173,7 +6263,7 @@ bool Application::canAcceptURL(const QString& urlString) const {
QUrl url(urlString);
if (url.query().contains(WEB_VIEW_TAG)) {
return false;
} else if (urlString.startsWith(HIFI_URL_SCHEME)) {
} else if (urlString.startsWith(URL_SCHEME_HIFI)) {
return true;
}
QString lowerPath = url.path().toLower();
@ -6186,15 +6276,14 @@ bool Application::canAcceptURL(const QString& urlString) const {
}
bool Application::acceptURL(const QString& urlString, bool defaultUpload) {
if (urlString.startsWith(HIFI_URL_SCHEME)) {
// this is a hifi URL - have the AddressManager handle it
emit receivedHifiSchemeURL(urlString);
QUrl url(urlString);
if (isDomainURL(url)) {
// this is a URL for a domain, either hifi:// or serverless - have the AddressManager handle it
QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",
Qt::AutoConnection, Q_ARG(const QString&, urlString));
return true;
}
QUrl url(urlString);
QString lowerPath = url.path().toLower();
for (auto& pair : _acceptedExtensions) {
if (lowerPath.endsWith(pair.first, Qt::CaseInsensitive)) {
@ -7033,7 +7122,7 @@ void Application::packageModel() {
void Application::openUrl(const QUrl& url) const {
if (!url.isEmpty()) {
if (url.scheme() == HIFI_URL_SCHEME) {
if (url.scheme() == URL_SCHEME_HIFI) {
DependencyManager::get<AddressManager>()->handleLookupString(url.toString());
} else {
// address manager did not handle - ask QDesktopServices to handle

View file

@ -284,6 +284,8 @@ public:
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
void saveNextPhysicsStats(QString filename);
bool isServerlessMode() const;
void replaceDomainContent(const QString& url);
signals:
@ -295,7 +297,6 @@ signals:
void activeDisplayPluginChanged();
void uploadRequest(QString path);
void receivedHifiSchemeURL(const QString& url);
public slots:
QVector<EntityItemID> pasteEntities(float x, float y, float z);
@ -391,6 +392,9 @@ public slots:
const QString getPreferredCursor() const { return _preferredCursor.get(); }
void setPreferredCursor(const QString& cursor);
void setIsServerlessMode(bool serverlessDomain);
void loadServerlessDomain(QUrl domainURL);
Q_INVOKABLE bool askBeforeSetAvatarUrl(const QString& avatarUrl) { return askToSetAvatarUrl(avatarUrl); }
private slots:
@ -425,7 +429,7 @@ private slots:
void setSessionUUID(const QUuid& sessionUUID) const;
void domainChanged(const QString& domainHostname);
void domainURLChanged(QUrl domainURL);
void updateWindowTitle() const;
void nodeAdded(SharedNodePointer node) const;
void nodeActivated(SharedNodePointer node);

View file

@ -49,7 +49,7 @@ void DiscoverabilityManager::updateLocation() {
auto accountManager = DependencyManager::get<AccountManager>();
auto addressManager = DependencyManager::get<AddressManager>();
auto& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
bool discoverable = (_mode.get() != Discoverability::None);
bool discoverable = (_mode.get() != Discoverability::None) && !domainHandler.isServerless();
if (accountManager->isLoggedIn()) {

View file

@ -129,7 +129,7 @@ int main(int argc, const char* argv[]) {
if (socket.waitForConnected(LOCAL_SERVER_TIMEOUT_MS)) {
if (parser.isSet(urlOption)) {
QUrl url = QUrl(parser.value(urlOption));
if (url.isValid() && url.scheme() == HIFI_URL_SCHEME) {
if (url.isValid() && url.scheme() == URL_SCHEME_HIFI) {
qDebug() << "Writing URL to local socket";
socket.write(url.toString().toUtf8());
if (!socket.waitForBytesWritten(5000)) {

View file

@ -124,7 +124,7 @@ void WindowScriptingInterface::promptAsync(const QString& message, const QString
}
void WindowScriptingInterface::disconnectedFromDomain() {
emit domainChanged("");
emit domainChanged(QUrl());
}
QString fixupPathForMac(const QString& directory) {

View file

@ -524,7 +524,7 @@ signals:
* Triggered when you change the domain you're visiting. <strong>Warning:</strong> Is not emitted if you go to domain that
* isn't running.
* @function Window.domainChanged
* @param {string} domain - The domain's IP address.
* @param {string} domainURL - The domain's URL.
* @returns {Signal}
* @example <caption>Report when you change domains.</caption>
* function onDomainChanged(domain) {
@ -533,7 +533,7 @@ signals:
*
* Window.domainChanged.connect(onDomainChanged);
*/
void domainChanged(const QString& domain);
void domainChanged(QUrl domainURL);
/**jsdoc
* Triggered when you try to navigate to a *.json, *.svo, or *.svo.json URL in a Web browser within Interface.

View file

@ -45,7 +45,6 @@ AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(pare
connect(&domainHandler, &DomainHandler::connectedToDomain, this, &AddressBarDialog::hostChanged);
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &AddressBarDialog::hostChanged);
connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::setUseFeed, this, &AddressBarDialog::setUseFeed);
connect(qApp, &Application::receivedHifiSchemeURL, this, &AddressBarDialog::receivedHifiSchemeURL);
}
void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) {

View file

@ -36,7 +36,6 @@ signals:
void backEnabledChanged();
void forwardEnabledChanged();
void useFeedChanged();
void receivedHifiSchemeURL(const QString& url);
void hostChanged();
protected:

View file

@ -221,8 +221,12 @@ void Avatar::updateAvatarEntities() {
return;
}
if (getID() == QUuid() || getID() == AVATAR_SELF_ID) {
return; // wait until MyAvatar gets an ID before doing this.
if (getID().isNull() ||
getID() == AVATAR_SELF_ID ||
DependencyManager::get<NodeList>()->getSessionUUID() == QUuid()) {
// wait until MyAvatar and this Node gets an ID before doing this. Otherwise, various things go wrong --
// things get their parent fixed up from AVATAR_SELF_ID to a null uuid which means "no parent".
return;
}
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
@ -1806,4 +1810,4 @@ scriptable::ScriptableModelBase Avatar::getScriptableModel() {
result.appendMaterials(_materials);
}
return result;
}
}

View file

@ -91,6 +91,11 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type,
return;
}
if (entityTree && entityTree->isServerlessMode()) {
// if we are in a serverless domain, don't send edit packets
return;
}
QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0);
if (type == PacketType::EntityAdd) {

View file

@ -963,7 +963,11 @@ void EntityItem::setHref(QString value) {
// If the string has something and doesn't start with with "hifi://" it shouldn't be set
// We allow the string to be empty, because that's the initial state of this property
if ( !(value.toLower().startsWith("hifi://")) && !value.isEmpty()) {
if (!value.isEmpty() &&
!(value.toLower().startsWith("hifi://")) &&
!(value.toLower().startsWith("file://"))
// TODO: serverless-domains will eventually support http and https also
) {
return;
}
withWriteLock([&] {

View file

@ -2864,6 +2864,9 @@ void EntityItemProperties::markAllChanged() {
_ambientLight.markAllChanged();
_skybox.markAllChanged();
_keyLightModeChanged = true;
_skyboxModeChanged = true;
_ambientLightModeChanged = true;
_hazeModeChanged = true;
_animation.markAllChanged();

View file

@ -596,7 +596,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) {
shouldDelete = false;
} else {
// only delete local entities, server entities will round trip through the server filters
if (entity->getClientOnly()) {
if (entity->getClientOnly() || _entityTree->isServerlessMode()) {
_entityTree->deleteEntity(entityID);
}
}
@ -1285,10 +1285,10 @@ bool EntityScriptingInterface::actionWorker(const QUuid& entityID,
}
doTransmit = actor(simulation, entity);
_entityTree->entityChanged(entity);
if (doTransmit) {
properties.setClientOnly(entity->getClientOnly());
properties.setOwningAvatarID(entity->getOwningAvatarID());
_entityTree->entityChanged(entity);
}
});

View file

@ -493,7 +493,7 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
if (!properties.getClientOnly() && getIsClient() &&
!nodeList->getThisNodeCanRez() && !nodeList->getThisNodeCanRezTmp() &&
!nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified()) {
!nodeList->getThisNodeCanRezCertified() && !nodeList->getThisNodeCanRezTmpCertified() && !_serverlessDomain) {
return nullptr;
}
@ -1509,7 +1509,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
}
if (isAdd && properties.getLocked() && !senderNode->isAllowedEditor()) {
// if a node can't change locks, don't allow them to create an already-locked entity
// if a node can't change locks, don't allow it to create an already-locked entity -- automatically
// clear the locked property and allow the unlocked entity to be created.
properties.setLocked(false);
bumpTimestamp(properties);
}
@ -2181,23 +2182,25 @@ QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSen
localTree->recurseTreeWithOperator(&moveOperator);
}
// send add-entity packets to the server
i = map.begin();
while (i != map.end()) {
EntityItemID newID = i.value();
EntityItemPointer entity = localTree->findEntityByEntityItemID(newID);
if (entity) {
// queue the packet to send to the server
entity->updateQueryAACube();
EntityItemProperties properties = entity->getProperties();
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
packetSender->queueEditEntityMessage(PacketType::EntityAdd, localTree, newID, properties);
i++;
} else {
i = map.erase(i);
if (!_serverlessDomain) {
// send add-entity packets to the server
i = map.begin();
while (i != map.end()) {
EntityItemID newID = i.value();
EntityItemPointer entity = localTree->findEntityByEntityItemID(newID);
if (entity) {
// queue the packet to send to the server
entity->updateQueryAACube();
EntityItemProperties properties = entity->getProperties();
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
packetSender->queueEditEntityMessage(PacketType::EntityAdd, localTree, newID, properties);
i++;
} else {
i = map.erase(i);
}
}
packetSender->releaseQueuedMessages();
}
packetSender->releaseQueuedMessages();
return map.values().toVector();
}

View file

@ -283,6 +283,9 @@ public:
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
void setIsServerlessMode(bool value) { _serverlessDomain = value; }
bool isServerlessMode() const { return _serverlessDomain; }
static void setAddMaterialToEntityOperator(std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> addMaterialToEntityOperator) { _addMaterialToEntityOperator = addMaterialToEntityOperator; }
static void setRemoveMaterialFromEntityOperator(std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> removeMaterialFromEntityOperator) { _removeMaterialFromEntityOperator = removeMaterialFromEntityOperator; }
static bool addMaterialToEntity(const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName);
@ -325,7 +328,7 @@ protected:
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);
bool isScriptInWhitelist(const QString& scriptURL);
QReadWriteLock _newlyCreatedHooksLock;
QVector<NewlyCreatedEntityHook*> _newlyCreatedHooks;
@ -412,6 +415,8 @@ private:
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromAvatarOperator;
static std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> _addMaterialToOverlayOperator;
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromOverlayOperator;
bool _serverlessDomain { false };
};
#endif // hifi_EntityTree_h

View file

@ -38,11 +38,13 @@
#include <NumericalConstants.h>
#include <shared/NsightHelpers.h>
#include <shared/FileUtils.h>
#include <PathUtils.h>
#include <Finally.h>
#include <Profile.h>
#include "NetworkLogging.h"
#include "ModelNetworkingLogging.h"
#include "NetworkingConstants.h"
#include <Trace.h>
#include <StatTracker.h>
@ -467,7 +469,7 @@ void NetworkTexture::makeLocalRequest() {
const QString scheme = _url.scheme();
QString path;
if (scheme == URL_SCHEME_FILE) {
path = _url.toLocalFile();
path = PathUtils::expandToLocalDataAbsolutePath(_url).toLocalFile();
} else {
path = ":" + _url.path();
}

View file

@ -22,6 +22,7 @@
#include <NumericalConstants.h>
#include <SettingHandle.h>
#include <UUID.h>
#include <PathUtils.h>
#include "AddressManager.h"
#include "NodeList.h"
@ -40,27 +41,14 @@ const QString SETTINGS_CURRENT_ADDRESS_KEY = "address";
Setting::Handle<QUrl> currentAddressHandle(QStringList() << ADDRESS_MANAGER_SETTINGS_GROUP << "address", DEFAULT_HIFI_ADDRESS);
AddressManager::AddressManager() :
_port(0)
{
}
bool AddressManager::isConnected() {
return DependencyManager::get<NodeList>()->getDomainHandler().isConnected();
}
QUrl AddressManager::currentAddress(bool domainOnly) const {
QUrl hifiURL;
QUrl hifiURL = _domainURL;
hifiURL.setScheme(HIFI_URL_SCHEME);
hifiURL.setHost(_host);
if (_port != 0 && _port != DEFAULT_DOMAIN_SERVER_PORT) {
hifiURL.setPort(_port);
}
if (!domainOnly) {
if (!domainOnly && hifiURL.scheme() == URL_SCHEME_HIFI) {
hifiURL.setPath(currentPath());
}
@ -69,7 +57,9 @@ QUrl AddressManager::currentAddress(bool domainOnly) const {
QUrl AddressManager::currentFacingAddress() const {
auto hifiURL = currentAddress();
hifiURL.setPath(currentFacingPath());
if (hifiURL.scheme() == URL_SCHEME_HIFI) {
hifiURL.setPath(currentFacingPath());
}
return hifiURL;
}
@ -79,7 +69,7 @@ QUrl AddressManager::currentShareableAddress(bool domainOnly) const {
// if we have a shareable place name use that instead of whatever the current host is
QUrl hifiURL;
hifiURL.setScheme(HIFI_URL_SCHEME);
hifiURL.setScheme(URL_SCHEME_HIFI);
hifiURL.setHost(_shareablePlaceName);
if (!domainOnly) {
@ -94,7 +84,9 @@ QUrl AddressManager::currentShareableAddress(bool domainOnly) const {
QUrl AddressManager::currentFacingShareableAddress() const {
auto hifiURL = currentShareableAddress();
hifiURL.setPath(currentFacingPath());
if (hifiURL.scheme() == URL_SCHEME_HIFI) {
hifiURL.setPath(currentFacingPath());
}
return hifiURL;
}
@ -139,11 +131,16 @@ void AddressManager::goForward() {
void AddressManager::storeCurrentAddress() {
auto url = currentAddress();
if (!url.host().isEmpty()) {
if (url.scheme() == URL_SCHEME_FILE ||
(url.scheme() == URL_SCHEME_HIFI && !url.host().isEmpty())) {
// TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can
// be loaded over http(s)
// url.scheme() == URL_SCHEME_HTTP ||
// url.scheme() == URL_SCHEME_HTTPS ||
currentAddressHandle.set(url);
} else {
qCWarning(networking) << "Ignoring attempt to save current address with an empty host" << url;
qCWarning(networking) << "Ignoring attempt to save current address with an invalid url:" << url;
}
}
@ -209,7 +206,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
static QString URL_TYPE_DOMAIN_ID = "domain_id";
static QString URL_TYPE_PLACE = "place";
static QString URL_TYPE_NETWORK_ADDRESS = "network_address";
if (lookupUrl.scheme() == HIFI_URL_SCHEME) {
if (lookupUrl.scheme() == URL_SCHEME_HIFI) {
qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString();
@ -289,12 +286,36 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
handlePath(lookupUrl.path(), trigger, true);
emit lookupResultsFinished();
return true;
} else if (lookupUrl.scheme() == URL_SCHEME_FILE) {
// TODO -- once Octree::readFromURL no-longer takes over the main event-loop, serverless-domain urls can
// be loaded over http(s)
// lookupUrl.scheme() == URL_SCHEME_HTTP ||
// lookupUrl.scheme() == URL_SCHEME_HTTPS ||
_previousLookup.clear();
QUrl domainURL = PathUtils::expandToLocalDataAbsolutePath(lookupUrl);
setDomainInfo(domainURL, trigger);
emit lookupResultsFinished();
handlePath(DOMAIN_SPAWNING_POINT, LookupTrigger::Internal, false);
return true;
}
return false;
}
bool isPossiblePlaceName(QString possiblePlaceName) {
bool result { false };
int length = possiblePlaceName.length();
static const int MINIMUM_PLACENAME_LENGTH = 1;
static const int MAXIMUM_PLACENAME_LENGTH = 64;
if (possiblePlaceName.toLower() != "localhost" &&
length >= MINIMUM_PLACENAME_LENGTH && length <= MAXIMUM_PLACENAME_LENGTH) {
const QRegExp PLACE_NAME_REGEX = QRegExp("^[0-9A-Za-z](([0-9A-Za-z]|-(?!-))*[^\\W_]$|$)");
result = PLACE_NAME_REGEX.indexIn(possiblePlaceName) == 0;
}
return result;
}
void AddressManager::handleLookupString(const QString& lookupString, bool fromSuggestions) {
if (!lookupString.isEmpty()) {
// make this a valid hifi URL and handle it off to handleUrl
@ -302,12 +323,16 @@ void AddressManager::handleLookupString(const QString& lookupString, bool fromSu
QUrl lookupURL;
if (!lookupString.startsWith('/')) {
const QRegExp HIFI_SCHEME_REGEX = QRegExp(HIFI_URL_SCHEME + ":\\/{1,2}", Qt::CaseInsensitive);
// sometimes we need to handle lookupStrings like hifi:/somewhere
const QRegExp HIFI_SCHEME_REGEX = QRegExp(URL_SCHEME_HIFI + ":\\/{1,2}", Qt::CaseInsensitive);
sanitizedString = sanitizedString.remove(HIFI_SCHEME_REGEX);
lookupURL = QUrl(HIFI_URL_SCHEME + "://" + sanitizedString);
lookupURL = QUrl(sanitizedString);
if (lookupURL.scheme().isEmpty()) {
lookupURL = QUrl("hifi://" + sanitizedString);
}
} else {
lookupURL = QUrl(lookupString);
lookupURL = QUrl(sanitizedString);
}
handleUrl(lookupURL, fromSuggestions ? Suggestions : UserInput);
@ -385,7 +410,11 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
qCDebug(networking) << "Possible domain change required to connect to" << domainHostname
<< "on" << domainPort;
emit possibleDomainChangeRequired(domainHostname, domainPort, domainID);
QUrl domainURL;
domainURL.setScheme(URL_SCHEME_HIFI);
domainURL.setHost(domainHostname);
domainURL.setPort(domainPort);
emit possibleDomainChangeRequired(domainURL, domainID);
} else {
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
@ -422,15 +451,10 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
if (setHost(placeName, trigger)) {
trigger = LookupTrigger::Internal;
}
_placeName = placeName;
} else {
if (setHost(domainIDString, trigger)) {
trigger = LookupTrigger::Internal;
}
// this isn't a place, so clear the place name
_placeName.clear();
}
// check if we had a path to override the path returned
@ -551,13 +575,17 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTri
if (ipAddressRegex.indexIn(lookupString) != -1) {
QString domainIPString = ipAddressRegex.cap(1);
qint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
if (!ipAddressRegex.cap(2).isEmpty()) {
domainPort = (qint16) ipAddressRegex.cap(2).toInt();
domainPort = (quint16) ipAddressRegex.cap(2).toInt();
}
emit lookupResultsFinished();
hostChanged = setDomainInfo(domainIPString, domainPort, trigger);
QUrl domainURL;
domainURL.setScheme(URL_SCHEME_HIFI);
domainURL.setHost(domainIPString);
domainURL.setPort(domainPort);
hostChanged = setDomainInfo(domainURL, trigger);
return true;
}
@ -570,11 +598,15 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTri
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
if (!hostnameRegex.cap(2).isEmpty()) {
domainPort = (qint16)hostnameRegex.cap(2).toInt();
domainPort = (quint16)hostnameRegex.cap(2).toInt();
}
emit lookupResultsFinished();
hostChanged = setDomainInfo(domainHostname, domainPort, trigger);
QUrl domainURL;
domainURL.setScheme(URL_SCHEME_HIFI);
domainURL.setHost(domainHostname);
domainURL.setPort(domainPort);
hostChanged = setDomainInfo(domainURL, trigger);
return true;
}
@ -643,7 +675,7 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should
addCurrentAddressToHistory(trigger);
}
if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) {
if (!isNaN(newPosition)) {
glm::quat newOrientation;
QRegExp orientationRegex(QUAT_REGEX_STRING);
@ -663,11 +695,11 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should
&& !isNaN(newOrientation.w)) {
orientationChanged = true;
} else {
qCDebug(networking) << "Orientation parsed from lookup string is invalid. Will not use for location change.";
qCDebug(networking) << "Orientation parsed from lookup string is invalid. Won't use for location change.";
}
}
emit locationChangeRequired(newPosition, orientationChanged,
emit locationChangeRequired(newPosition, orientationChanged,
trigger == LookupTrigger::VisitUserFromPAL ? cancelOutRollAndPitch(newOrientation): newOrientation,
shouldFace
);
@ -698,18 +730,20 @@ bool AddressManager::handleUsername(const QString& lookupString) {
}
bool AddressManager::setHost(const QString& host, LookupTrigger trigger, quint16 port) {
if (host != _host || port != _port) {
if (host != _domainURL.host() || port != _domainURL.port()) {
addCurrentAddressToHistory(trigger);
_port = port;
bool emitHostChanged = host != _domainURL.host();
_domainURL = QUrl();
_domainURL.setScheme(URL_SCHEME_HIFI);
_domainURL.setHost(host);
_domainURL.setPort(port);
// any host change should clear the shareable place name
_shareablePlaceName.clear();
if (host != _host) {
_host = host;
emit hostChanged(_host);
if (emitHostChanged) {
emit hostChanged(host);
}
return true;
@ -718,20 +752,43 @@ bool AddressManager::setHost(const QString& host, LookupTrigger trigger, quint16
return false;
}
bool AddressManager::setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger) {
bool hostChanged = setHost(hostname, trigger, port);
QString AddressManager::getHost() const {
if (isPossiblePlaceName(_domainURL.host())) {
return QString();
}
return _domainURL.host();
}
bool AddressManager::setDomainInfo(const QUrl& domainURL, LookupTrigger trigger) {
const QString hostname = domainURL.host();
quint16 port = domainURL.port();
bool emitHostChanged { false };
if (domainURL != _domainURL) {
addCurrentAddressToHistory(trigger);
emitHostChanged = true;
}
_domainURL = domainURL;
// clear any current place information
_rootPlaceID = QUuid();
_placeName.clear();
qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port;
if (_domainURL.scheme() == URL_SCHEME_HIFI) {
qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port;
} else {
qCDebug(networking) << "Possible domain change required to serverless domain: " << domainURL.toString();
}
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress);
emit possibleDomainChangeRequired(hostname, port, QUuid());
if (emitHostChanged) {
emit hostChanged(domainURL.host());
}
emit possibleDomainChangeRequired(_domainURL, QUuid());
return hostChanged;
return emitHostChanged;
}
void AddressManager::goToUser(const QString& username, bool shouldMatchOrientation) {
@ -820,7 +877,7 @@ void AddressManager::lookupShareableNameForDomainID(const QUuid& domainID) {
// then use that for Steam join/invite or copiable address
// it only makes sense to lookup a shareable default name if we don't have a place name
if (_placeName.isEmpty()) {
if (getPlaceName().isEmpty()) {
JSONCallbackParameters callbackParams;
// no error callback handling
@ -872,3 +929,12 @@ void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) {
}
}
QString AddressManager::getPlaceName() const {
if (!_shareablePlaceName.isEmpty()) {
return _shareablePlaceName;
}
if (isPossiblePlaceName(_domainURL.host())) {
return _domainURL.host();
}
return QString();
}

View file

@ -22,8 +22,6 @@
#include "AccountManager.h"
const QString HIFI_URL_SCHEME = "hifi";
extern const QString DEFAULT_HIFI_ADDRESS;
const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost";
@ -147,7 +145,7 @@ public:
};
bool isConnected();
const QString& getProtocol() { return HIFI_URL_SCHEME; };
const QString& getProtocol() { return URL_SCHEME_HIFI; };
QUrl currentAddress(bool domainOnly = false) const;
QUrl currentFacingAddress() const;
@ -157,10 +155,10 @@ public:
QString currentFacingPath() const;
const QUuid& getRootPlaceID() const { return _rootPlaceID; }
const QString& getPlaceName() const { return _shareablePlaceName.isEmpty() ? _placeName : _shareablePlaceName; }
QString getPlaceName() const;
QString getDomainID() const;
const QString& getHost() const { return _host; }
QString getHost() const;
void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; }
void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
@ -170,6 +168,8 @@ public:
const QStack<QUrl>& getBackStack() const { return _backStack; }
const QStack<QUrl>& getForwardStack() const { return _forwardStack; }
QUrl getDomainURL() { return _domainURL; }
public slots:
/**jsdoc
* Go to a specified metaverse address.
@ -302,13 +302,12 @@ signals:
/**jsdoc
* Triggered when a request is made to go to an IP address.
* @function location.possibleDomainChangeRequired
* @param {string} hostName - The name of the domain to go do.
* @param {number} port - The integer number of the network port to connect to.
* @param {Url} domainURL - URL for domain
* @param {Uuid} domainID - The UUID of the domain to go to.
* @returns {Signal}
*/
// No example because this function isn't typically used in scripts.
void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort, const QUuid& domainID);
void possibleDomainChangeRequired(QUrl domainURL, QUuid domainID);
/**jsdoc
* Triggered when a request is made to go to a named domain or user.
@ -360,7 +359,7 @@ signals:
* location.pathChangeRequired.connect(onPathChangeRequired);
*/
void pathChangeRequired(const QString& newPath);
/**jsdoc
* Triggered when you navigate to a new domain.
* @function location.hostChanged
@ -392,7 +391,7 @@ signals:
void goBackPossible(bool isPossible);
/**jsdoc
* Triggered when there's a change in whether or not there's a forward location that can be navigated to using
* Triggered when there's a change in whether or not there's a forward location that can be navigated to using
* {@link location.goForward|goForward}. (Reflects changes in the state of the "Goto" dialog's forward arrow.)
* @function location.goForwardPossible
* @param {boolean} isPossible - <code>true</code> if there's a forward location to navigate to, otherwise
@ -407,8 +406,6 @@ signals:
*/
void goForwardPossible(bool isPossible);
protected:
AddressManager();
private slots:
void handleAPIResponse(QNetworkReply& requestReply);
void handleAPIError(QNetworkReply& errorReply);
@ -420,7 +417,7 @@ private:
// Set host and port, and return `true` if it was changed.
bool setHost(const QString& host, LookupTrigger trigger, quint16 port = 0);
bool setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger);
bool setDomainInfo(const QUrl& domainURL, LookupTrigger trigger);
const JSONCallbackParameters& apiCallbackParameters();
@ -438,9 +435,8 @@ private:
void addCurrentAddressToHistory(LookupTrigger trigger);
QString _host;
quint16 _port;
QString _placeName;
QUrl _domainURL;
QUuid _rootPlaceID;
PositionGetter _positionGetter;
OrientationGetter _orientationGetter;
@ -452,7 +448,7 @@ private:
quint64 _lastBackPush = 0;
QString _newHostLookupPath;
QUrl _previousLookup;
};

View file

@ -20,6 +20,7 @@
#include "NetworkAccessManager.h"
#include "NetworkLogging.h"
#include "NetworkingConstants.h"
#include "ResourceManager.h"

View file

@ -38,7 +38,7 @@ DomainHandler::DomainHandler(QObject* parent) :
// if we get a socket that make sure our NetworkPeer ping timer stops
connect(this, &DomainHandler::completedSocketDiscovery, &_icePeer, &NetworkPeer::stopPingTimer);
// setup a timeout for failure on settings requests
static const int DOMAIN_SETTINGS_TIMEOUT_MS = 5000;
_settingsTimer.setInterval(DOMAIN_SETTINGS_TIMEOUT_MS); // 5s, Qt::CoarseTimer acceptable
@ -60,11 +60,11 @@ void DomainHandler::disconnect() {
if (_isConnected) {
sendDisconnectPacket();
}
// clear member variables that hold the connection state to a domain
_uuid = QUuid();
_connectionToken = QUuid();
_icePeer.reset();
if (requiresICE()) {
@ -78,10 +78,10 @@ void DomainHandler::disconnect() {
void DomainHandler::sendDisconnectPacket() {
// The DomainDisconnect packet is not verified - we're relying on the eventual addition of DTLS to the
// domain-server connection to stop greifing here
// construct the disconnect packet once (an empty packet but sourced with our current session UUID)
static auto disconnectPacket = NLPacket::create(PacketType::DomainDisconnectRequest, 0);
// send the disconnect packet to the current domain server
auto nodeList = DependencyManager::get<NodeList>();
nodeList->sendUnreliablePacket(*disconnectPacket, _sockAddr);
@ -94,7 +94,7 @@ void DomainHandler::clearSettings() {
void DomainHandler::softReset() {
qCDebug(networking) << "Resetting current domain connection information.";
disconnect();
clearSettings();
_connectionDenialsSinceKeypairRegen = 0;
@ -115,8 +115,8 @@ void DomainHandler::hardReset() {
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
_pendingDomainID = QUuid();
_iceServerSockAddr = HifiSockAddr();
_hostname = QString();
_sockAddr.clear();
_domainURL = QUrl();
_domainConnectionRefusals.clear();
@ -139,7 +139,10 @@ void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hos
}
// some callers may pass a hostname, this is not to be used for lookup but for DTLS certificate verification
_hostname = hostname;
_domainURL = QUrl();
_domainURL.setScheme(URL_SCHEME_HIFI);
_domainURL.setHost(hostname);
_domainURL.setPort(_sockAddr.getPort());
}
void DomainHandler::setUUID(const QUuid& uuid) {
@ -149,36 +152,45 @@ void DomainHandler::setUUID(const QUuid& uuid) {
}
}
void DomainHandler::setSocketAndID(const QString& hostname, quint16 port, const QUuid& domainID) {
void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) {
_pendingDomainID = domainID;
if (hostname != _hostname || _sockAddr.getPort() != port) {
if (domainURL.scheme() != URL_SCHEME_HIFI) {
_sockAddr.clear();
}
if (_domainURL != domainURL || _sockAddr.getPort() != domainURL.port()) {
// re-set the domain info so that auth information is reloaded
hardReset();
if (hostname != _hostname) {
// set the new hostname
_hostname = hostname;
QString previousHost = _domainURL.host();
_domainURL = domainURL;
qCDebug(networking) << "Updated domain hostname to" << _hostname;
if (domainURL.scheme() != URL_SCHEME_HIFI) {
setIsConnected(true);
} else if (previousHost != domainURL.host()) {
qCDebug(networking) << "Updated domain hostname to" << domainURL.host();
// re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname
qCDebug(networking, "Looking up DS hostname %s.", _hostname.toLocal8Bit().constData());
QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&)));
if (!domainURL.host().isEmpty()) {
// re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname
qCDebug(networking, "Looking up DS hostname %s.", domainURL.host().toLocal8Bit().constData());
QHostInfo::lookupHost(domainURL.host(), this, SLOT(completedHostnameLookup(const QHostInfo&)));
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainHostname);
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(
LimitedNodeList::ConnectionStep::SetDomainHostname);
UserActivityLogger::getInstance().changedDomain(_hostname);
emit hostnameChanged(_hostname);
UserActivityLogger::getInstance().changedDomain(domainURL.host());
}
}
if (_sockAddr.getPort() != port) {
qCDebug(networking) << "Updated domain port to" << port;
emit domainURLChanged(_domainURL);
if (_sockAddr.getPort() != domainURL.port()) {
qCDebug(networking) << "Updated domain port to" << domainURL.port();
}
// grab the port by reading the string after the colon
_sockAddr.setPort(port);
_sockAddr.setPort(domainURL.port());
}
}
@ -187,10 +199,10 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
if (_iceServerSockAddr.getAddress().toString() != iceServerHostname || id != _pendingDomainID) {
// re-set the domain info to connect to new domain
hardReset();
// refresh our ICE client UUID to something new
_iceClientID = QUuid::createUuid();
_pendingDomainID = id;
HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr;
@ -216,14 +228,18 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
void DomainHandler::activateICELocalSocket() {
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket);
_sockAddr = _icePeer.getLocalSocket();
_hostname = _sockAddr.getAddress().toString();
_domainURL.setScheme(URL_SCHEME_HIFI);
_domainURL.setHost(_sockAddr.getAddress().toString());
emit domainURLChanged(_domainURL);
emit completedSocketDiscovery();
}
void DomainHandler::activateICEPublicSocket() {
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket);
_sockAddr = _icePeer.getPublicSocket();
_hostname = _sockAddr.getAddress().toString();
_domainURL.setScheme(URL_SCHEME_HIFI);
_domainURL.setHost(_sockAddr.getAddress().toString());
emit domainURLChanged(_domainURL);
emit completedSocketDiscovery();
}
@ -234,7 +250,7 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket);
qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(),
qCDebug(networking, "DS at %s is at %s", _domainURL.host().toLocal8Bit().constData(),
_sockAddr.getAddress().toString().toLocal8Bit().constData());
emit completedSocketDiscovery();
@ -261,10 +277,12 @@ void DomainHandler::setIsConnected(bool isConnected) {
_isConnected = isConnected;
if (_isConnected) {
emit connectedToDomain(_hostname);
emit connectedToDomain(_domainURL);
// we've connected to new domain - time to ask it for global settings
requestDomainSettings();
if (_domainURL.scheme() == URL_SCHEME_HIFI && !_domainURL.host().isEmpty()) {
// we've connected to new domain - time to ask it for global settings
requestDomainSettings();
}
} else {
emit disconnectedFromDomain();

View file

@ -25,6 +25,7 @@
#include "NLPacketList.h"
#include "Node.h"
#include "ReceivedMessage.h"
#include "NetworkingConstants.h"
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103;
@ -37,14 +38,14 @@ class DomainHandler : public QObject {
Q_OBJECT
public:
DomainHandler(QObject* parent = 0);
void disconnect();
void clearSettings();
const QUuid& getUUID() const { return _uuid; }
void setUUID(const QUuid& uuid);
const QString& getHostname() const { return _hostname; }
QString getHostname() const { return _domainURL.host(); }
const QHostAddress& getIP() const { return _sockAddr.getAddress(); }
void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); }
@ -57,7 +58,7 @@ public:
const QUuid& getConnectionToken() const { return _connectionToken; }
void setConnectionToken(const QUuid& connectionToken) { _connectionToken = connectionToken; }
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
@ -73,11 +74,12 @@ public:
bool isConnected() const { return _isConnected; }
void setIsConnected(bool isConnected);
bool isServerless() const { return _domainURL.scheme() != URL_SCHEME_HIFI; }
bool hasSettings() const { return !_settingsObject.isEmpty(); }
void requestDomainSettings();
const QJsonObject& getSettingsObject() const { return _settingsObject; }
void setPendingPath(const QString& pendingPath) { _pendingPath = pendingPath; }
const QString& getPendingPath() { return _pendingPath; }
void clearPendingPath() { _pendingPath.clear(); }
@ -139,7 +141,7 @@ public:
};
public slots:
void setSocketAndID(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT, const QUuid& id = QUuid());
void setURLAndID(QUrl domainURL, QUuid id);
void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id);
void processSettingsPacketList(QSharedPointer<ReceivedMessage> packetList);
@ -153,14 +155,14 @@ private slots:
void completedIceServerHostnameLookup();
signals:
void hostnameChanged(const QString& hostname);
void domainURLChanged(QUrl domainURL);
// NOTE: the emission of completedSocketDiscovery does not mean a connection to DS is established
// It means that, either from DNS lookup or ICE, we think we have a socket we can talk to DS on
void completedSocketDiscovery();
void resetting();
void connectedToDomain(const QString& hostname);
void connectedToDomain(QUrl domainURL);
void disconnectedFromDomain();
void iceSocketAndIDReceived();
@ -179,7 +181,7 @@ private:
void hardReset();
QUuid _uuid;
QString _hostname;
QUrl _domainURL;
HifiSockAddr _sockAddr;
QUuid _assignmentUUID;
QUuid _connectionToken;
@ -200,4 +202,7 @@ private:
QTimer _apiRefreshTimer;
};
const QString DOMAIN_SPAWNING_POINT { "/0, -10, 0" };
#endif // hifi_DomainHandler_h

View file

@ -17,9 +17,11 @@
#include <StatTracker.h>
#include <shared/FileUtils.h>
#include <PathUtils.h>
#include "NetworkLogging.h"
#include "ResourceManager.h"
#include "NetworkingConstants.h"
void FileResourceRequest::doSend() {
auto statTracker = DependencyManager::get<StatTracker>();
@ -29,7 +31,7 @@ void FileResourceRequest::doSend() {
if (_url.scheme() == URL_SCHEME_QRC) {
filename = ":/" + _url.path();
} else {
filename = _url.toLocalFile();
filename = PathUtils::expandToLocalDataAbsolutePath(_url).toLocalFile();
// sometimes on windows, we see the toLocalFile() return null,
// in this case we will attempt to simply use the url as a string
if (filename.isEmpty()) {

View file

@ -0,0 +1,24 @@
//
// NetworkingConstants.cpp
// libraries/networking/src
//
// Created by Seth Alves on 2018-2-28.
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "NetworkingConstants.h"
namespace NetworkingConstants {
// You can change the return of this function if you want to use a custom metaverse URL at compile time
// or you can pass a custom URL via the env variable
QUrl METAVERSE_SERVER_URL() {
const QString HIFI_METAVERSE_URL_ENV = "HIFI_METAVERSE_URL";
const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains(HIFI_METAVERSE_URL_ENV)
? QUrl(QProcessEnvironment::systemEnvironment().value(HIFI_METAVERSE_URL_ENV))
: METAVERSE_SERVER_URL_STABLE;
return serverURL;
};
}

View file

@ -25,18 +25,17 @@ namespace NetworkingConstants {
// if you manually generate a personal access token for the domains scope
// at https://staging.highfidelity.com/user/tokens/new?for_domain_server=true
const QUrl METAVERSE_SERVER_URL_STABLE("https://metaverse.highfidelity.com");
const QUrl METAVERSE_SERVER_URL_STAGING("https://staging.highfidelity.com");
// You can change the return of this function if you want to use a custom metaverse URL at compile time
// or you can pass a custom URL via the env variable
static const QUrl METAVERSE_SERVER_URL() {
static const QString HIFI_METAVERSE_URL_ENV = "HIFI_METAVERSE_URL";
static const QUrl serverURL = QProcessEnvironment::systemEnvironment().contains(HIFI_METAVERSE_URL_ENV)
? QUrl(QProcessEnvironment::systemEnvironment().value(HIFI_METAVERSE_URL_ENV))
: METAVERSE_SERVER_URL_STABLE;
return serverURL;
};
const QUrl METAVERSE_SERVER_URL_STABLE { "https://metaverse.highfidelity.com" };
const QUrl METAVERSE_SERVER_URL_STAGING { "https://staging.highfidelity.com" };
QUrl METAVERSE_SERVER_URL();
}
const QString URL_SCHEME_HIFI = "hifi";
const QString URL_SCHEME_QRC = "qrc";
const QString URL_SCHEME_FILE = "file";
const QString URL_SCHEME_HTTP = "http";
const QString URL_SCHEME_HTTPS = "https";
const QString URL_SCHEME_FTP = "ftp";
const QString URL_SCHEME_ATP = "atp";
#endif // hifi_NetworkingConstants_h

View file

@ -55,7 +55,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
// handle domain change signals from AddressManager
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequired,
&_domainHandler, &DomainHandler::setSocketAndID);
&_domainHandler, &DomainHandler::setURLAndID);
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID,
&_domainHandler, &DomainHandler::setIceServerHostnameAndID);
@ -91,7 +91,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
connect(accountManager.data(), &AccountManager::newKeypair, this, &NodeList::sendDomainServerCheckIn);
// clear out NodeList when login is finished
connect(accountManager.data(), SIGNAL(loginComplete()) , this, SLOT(reset()));
connect(accountManager.data(), SIGNAL(loginComplete(const QUrl&)) , this, SLOT(reset()));
// clear our NodeList when logout is requested
connect(accountManager.data(), SIGNAL(logoutComplete()) , this, SLOT(reset()));
@ -106,7 +106,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
// setup our timer to send keepalive pings (it's started and stopped on domain connect/disconnect)
_keepAlivePingTimer.setInterval(KEEPALIVE_PING_INTERVAL_MS); // 1s, Qt::CoarseTimer acceptable
connect(&_keepAlivePingTimer, &QTimer::timeout, this, &NodeList::sendKeepAlivePings);
connect(&_domainHandler, SIGNAL(connectedToDomain(QString)), &_keepAlivePingTimer, SLOT(start()));
connect(&_domainHandler, SIGNAL(connectedToDomain(QUrl)), &_keepAlivePingTimer, SLOT(start()));
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, &_keepAlivePingTimer, &QTimer::stop);
// set our sockAddrBelongsToDomainOrNode method as the connection creation filter for the udt::Socket

View file

@ -22,13 +22,6 @@
#include "ResourceRequest.h"
const QString URL_SCHEME_QRC = "qrc";
const QString URL_SCHEME_FILE = "file";
const QString URL_SCHEME_HTTP = "http";
const QString URL_SCHEME_HTTPS = "https";
const QString URL_SCHEME_FTP = "ftp";
const QString URL_SCHEME_ATP = "atp";
class ResourceManager: public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY

View file

@ -1685,6 +1685,15 @@ bool Octree::readFromURL(const QString& urlString) {
}
auto data = request->getData();
QByteArray uncompressedJsonData;
bool wasCompressed = gunzip(data, uncompressedJsonData);
if (wasCompressed) {
QDataStream inputStream(uncompressedJsonData);
return readFromStream(uncompressedJsonData.size(), inputStream, marketplaceID);
}
QDataStream inputStream(data);
return readFromStream(data.size(), inputStream, marketplaceID);
}

View file

@ -81,7 +81,7 @@ const QString& PathUtils::resourcesPath() {
#else
staticResourcePath = ":/";
#endif
#if !defined(Q_OS_ANDROID) && defined(DEV_BUILD)
if (USE_SOURCE_TREE_RESOURCES()) {
// For dev builds, optionally load content from the Git source tree
@ -120,6 +120,31 @@ QUrl PathUtils::resourcesUrl(const QString& relativeUrl) {
return QUrl(resourcesUrl() + relativeUrl);
}
QUrl PathUtils::expandToLocalDataAbsolutePath(const QUrl& fileUrl) {
QString path = fileUrl.path();
if (path.startsWith("/~/")) {
// this results in a qrc:// url...
// return resourcesUrl(path.mid(3));
#ifdef Q_OS_MAC
static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/";
#elif defined (ANDROID)
static const QString staticResourcePath =
QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources/";
#else
static const QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/";
#endif
path.replace(0, 3, staticResourcePath);
QUrl expandedURL = QUrl::fromLocalFile(path);
return expandedURL;
}
QUrl::fromLocalFile(resourcesPath()).toString();
return fileUrl;
}
const QString& PathUtils::qmlBaseUrl() {
static const QString staticResourcePath = resourcesUrl() + "qml/";
return staticResourcePath;

View file

@ -37,6 +37,7 @@ public:
static QUrl resourcesUrl(const QString& relative);
static const QString& resourcesPath();
static const QString& qmlBaseUrl();
static QUrl expandToLocalDataAbsolutePath(const QUrl& fileUrl);
static QUrl qmlUrl(const QString& relative);
#ifdef DEV_BUILD
static const QString& projectRootPath();

View file

@ -119,7 +119,7 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
nodeList->startThread();
const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainChanged(QUrl)));
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ACClientApp::domainConnectionRefused);
connect(nodeList.data(), &NodeList::nodeAdded, this, &ACClientApp::nodeAdded);
@ -169,7 +169,7 @@ void ACClientApp::domainConnectionRefused(const QString& reasonMessage, int reas
qDebug() << "domainConnectionRefused";
}
void ACClientApp::domainChanged(const QString& domainHostname) {
void ACClientApp::domainChanged(QUrl domainURL) {
if (_verbose) {
qDebug() << "domainChanged";
}

View file

@ -29,7 +29,7 @@ public:
private slots:
void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo);
void domainChanged(const QString& domainHostname);
void domainChanged(QUrl domainURL);
void nodeAdded(SharedNodePointer node);
void nodeActivated(SharedNodePointer node);
void nodeKilled(SharedNodePointer node);

View file

@ -158,7 +158,7 @@ ATPClientApp::ATPClientApp(int argc, char* argv[]) :
nodeList->startThread();
const DomainHandler& domainHandler = nodeList->getDomainHandler();
connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));
connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainChanged(QUrl)));
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ATPClientApp::domainConnectionRefused);
connect(nodeList.data(), &NodeList::nodeAdded, this, &ATPClientApp::nodeAdded);
@ -227,7 +227,7 @@ void ATPClientApp::domainConnectionRefused(const QString& reasonMessage, int rea
}
}
void ATPClientApp::domainChanged(const QString& domainHostname) {
void ATPClientApp::domainChanged(QUrl domainURL) {
if (_verbose) {
qDebug() << "domainChanged";
}

View file

@ -31,7 +31,7 @@ public:
private slots:
void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo);
void domainChanged(const QString& domainHostname);
void domainChanged(QUrl domainURL);
void nodeAdded(SharedNodePointer node);
void nodeActivated(SharedNodePointer node);
void nodeKilled(SharedNodePointer node);