5476 lines · 222.9 KB · C++
RawDownload
    _enableProcessOctreeThread(true),    _lastNackTime(usecTimestampNow()),    _lastSendDownstreamAudioStats(usecTimestampNow()),    _aboutToQuit(false),    _notifiedPacketVersionMismatchThisDomain(false),    _maxOctreePPS(maxOctreePacketsPerSecond.get()),    _lastFaceTrackerUpdate(0){      PluginContainer* pluginContainer = dynamic_cast<PluginContainer*>(this); // set the container for any plugins that care    PluginManager::getInstance()->setContainer(pluginContainer);     // FIXME this may be excessively conservative.  On the other hand    // maybe I'm used to having an 8-core machine    // Perhaps find the ideal thread count  and subtract 2 or 3    // (main thread, present thread, random OS load)    // More threads == faster concurrent loads, but also more concurrent    // load on the GPU until we can serialize GPU transfers (off the main thread)    QThreadPool::globalInstance()->setMaxThreadCount(2);    thread()->setPriority(QThread::HighPriority);    thread()->setObjectName("Main Thread");     setInstance(this);     auto controllerScriptingInterface = DependencyManager::get<controller::ScriptingInterface>().data();    _controllerScriptingInterface = dynamic_cast<ControllerScriptingInterface*>(controllerScriptingInterface);     _entityClipboard->createRootElement(); #ifdef Q_OS_WIN    installNativeEventFilter(&MyNativeEventFilter::getInstance());#endif     _logger = new FileLogger(this);  // After setting organization name in order to get correct directory     qInstallMessageHandler(messageHandler);     QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf");    _window->setWindowTitle("Interface");     Model::setAbstractViewStateInterface(this); // The model class will sometimes need to know view state details from us     auto nodeList = DependencyManager::get<NodeList>();     // Set up a watchdog thread to intentionally crash the application on deadlocks    _deadlockWatchdogThread = new DeadlockWatchdogThread();    _deadlockWatchdogThread->start();     qCDebug(interfaceapp) << "[VERSION] Build sequence:" << qPrintable(applicationVersion());     _bookmarks = new Bookmarks();  // Before setting up the menu     // start the nodeThread so its event loop is running    QThread* nodeThread = new QThread(this);    nodeThread->setObjectName("NodeList Thread");    nodeThread->start();     // make sure the node thread is given highest priority    nodeThread->setPriority(QThread::TimeCriticalPriority);     // setup a timer for domain-server check ins    QTimer* domainCheckInTimer = new QTimer(nodeList.data());    connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn);    domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS);     // put the NodeList and datagram processing on the node thread    nodeList->moveToThread(nodeThread);     // put the audio processing on a separate thread    QThread* audioThread = new QThread();    audioThread->setObjectName("Audio Thread");     auto audioIO = DependencyManager::get<AudioClient>();    audioIO->setPositionGetter([]{        auto avatarManager = DependencyManager::get<AvatarManager>();        auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;                return myAvatar ? myAvatar->getPositionForAudio() : Vectors::ZERO;    });    audioIO->setOrientationGetter([]{        auto avatarManager = DependencyManager::get<AvatarManager>();        auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;         return myAvatar ? myAvatar->getOrientationForAudio() : Quaternions::IDENTITY;    });     audioIO->moveToThread(audioThread);    recording::Frame::registerFrameHandler(AudioConstants::getAudioFrameName(), [=](recording::Frame::ConstPointer frame) {        audioIO->handleRecordedAudioInput(frame->data);    });     connect(audioIO.data(), &AudioClient::inputReceived, [](const QByteArray& audio){        static auto recorder = DependencyManager::get<recording::Recorder>();        if (recorder->isRecording()) {            static const recording::FrameType AUDIO_FRAME_TYPE = recording::Frame::registerFrameType(AudioConstants::getAudioFrameName());            recorder->recordFrame(AUDIO_FRAME_TYPE, audio);        }    });     auto& audioScriptingInterface = AudioScriptingInterface::getInstance();    connect(audioThread, &QThread::started, audioIO.data(), &AudioClient::start);    connect(audioIO.data(), &AudioClient::destroyed, audioThread, &QThread::quit);    connect(audioThread, &QThread::finished, audioThread, &QThread::deleteLater);    connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled);    connect(audioIO.data(), &AudioClient::mutedByMixer, &audioScriptingInterface, &AudioScriptingInterface::mutedByMixer);    connect(audioIO.data(), &AudioClient::receivedFirstPacket, &audioScriptingInterface, &AudioScriptingInterface::receivedFirstPacket);    connect(audioIO.data(), &AudioClient::disconnected, &audioScriptingInterface, &AudioScriptingInterface::disconnected);    connect(audioIO.data(), &AudioClient::muteEnvironmentRequested, [](glm::vec3 position, float radius) {        auto audioClient = DependencyManager::get<AudioClient>();        auto myAvatarPosition = DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition();        float distance = glm::distance(myAvatarPosition, position);        bool shouldMute = !audioClient->isMuted() && (distance < radius);         if (shouldMute) {            audioClient->toggleMute();            AudioScriptingInterface::getInstance().environmentMuted();        }    });     audioThread->start();     ResourceManager::init();    // Make sure we don't time out during slow operations at startup    updateHeartbeat();     // Setup MessagesClient    auto messagesClient = DependencyManager::get<MessagesClient>();    QThread* messagesThread = new QThread;    messagesThread->setObjectName("Messages Client Thread");    messagesClient->moveToThread(messagesThread);    connect(messagesThread, &QThread::started, messagesClient.data(), &MessagesClient::init);    messagesThread->start();     const DomainHandler& domainHandler = nodeList->getDomainHandler();     connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&)));    connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain()));    connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle()));    connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));    connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));    connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &Application::domainConnectionRefused);     // update our location every 5 seconds in the metaverse server, assuming that we are authenticated with one    const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * MSECS_PER_SECOND;     auto discoverabilityManager = DependencyManager::get<DiscoverabilityManager>();    connect(&locationUpdateTimer, &QTimer::timeout, discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);    connect(&locationUpdateTimer, &QTimer::timeout,        DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);    locationUpdateTimer.start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS);     // if we get a domain change, immediately attempt update location in metaverse server    connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain,        discoverabilityManager.data(), &DiscoverabilityManager::updateLocation);     // send a location update immediately    discoverabilityManager->updateLocation();     connect(nodeList.data(), &NodeList::nodeAdded, this, &Application::nodeAdded);    connect(nodeList.data(), &NodeList::nodeKilled, this, &Application::nodeKilled);    connect(nodeList.data(), &NodeList::nodeActivated, this, &Application::nodeActivated);    connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID);    connect(nodeList.data(), &NodeList::uuidChanged, this, &Application::setSessionUUID);    connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &Application::notifyPacketVersionMismatch);     // you might think we could just do this in NodeList but we only want this connection for Interface    connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset);     // connect to appropriate slots on AccountManager    auto accountManager = DependencyManager::get<AccountManager>();     auto dialogsManager = DependencyManager::get<DialogsManager>();    connect(accountManager.data(), &AccountManager::authRequired, dialogsManager.data(), &DialogsManager::showLoginDialog);    connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);     // set the account manager's root URL and trigger a login request if we don't have the access token    accountManager->setIsAgent(true);    accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL);     auto addressManager = DependencyManager::get<AddressManager>();     // use our MyAvatar position and quat for address manager path    addressManager->setPositionGetter([this]{ return getMyAvatar()->getPosition(); });    addressManager->setOrientationGetter([this]{ return getMyAvatar()->getOrientation(); });     connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle);    connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress);     // Save avatar location immediately after a teleport.    connect(getMyAvatar(), &MyAvatar::positionGoneTo,        DependencyManager::get<AddressManager>().data(), &AddressManager::storeCurrentAddress);     auto scriptEngines = DependencyManager::get<ScriptEngines>().data();    scriptEngines->registerScriptInitializer([this](ScriptEngine* engine){        registerScriptEngineWithApplicationServices(engine);    });     connect(scriptEngines, &ScriptEngines::scriptCountChanged, scriptEngines, [this] {        auto scriptEngines = DependencyManager::get<ScriptEngines>();        if (scriptEngines->getRunningScripts().isEmpty()) {            getMyAvatar()->clearScriptableSettings();        }    }, Qt::QueuedConnection);     connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] {        getEntities()->reloadEntityScripts();    }, Qt::QueuedConnection);     connect(scriptEngines, &ScriptEngines::scriptLoadError,        scriptEngines, [](const QString& filename, const QString& error){        OffscreenUi::warning(nullptr, "Error Loading Script", filename + " failed to load.");    }, Qt::QueuedConnection); #ifdef _WIN32    WSADATA WsaData;    int wsaresult = WSAStartup(MAKEWORD(2, 2), &WsaData);#endif     // tell the NodeList instance who to tell the domain server we care about    nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer        << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer);     // connect to the packet sent signal of the _entityEditSender    connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent);     // send the identity packet for our avatar each second to our avatar mixer    connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket);    identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS);     ResourceCache::setRequestLimit(3);     _glWidget = new GLCanvas();    getApplicationCompositor().setRenderingWidget(_glWidget);    _window->setCentralWidget(_glWidget);     _window->restoreGeometry();    _window->setVisible(true);     _glWidget->setFocusPolicy(Qt::StrongFocus);    _glWidget->setFocus(); #ifdef Q_OS_MAC    auto cursorTarget = _window; // OSX doesn't seem to provide for hiding the cursor only on the GL widget#else    // On windows and linux, hiding the top level cursor also means it's invisible when hovering over the     // window menu, which is a pain, so only hide it for the GL surface    auto cursorTarget = _glWidget;#endif    cursorTarget->setCursor(Qt::BlankCursor);     // enable mouse tracking; otherwise, we only get drag events    _glWidget->setMouseTracking(true);    _glWidget->makeCurrent();    _glWidget->initializeGL();     _chromiumShareContext = new OffscreenGLCanvas();    _chromiumShareContext->create(_glWidget->context()->contextHandle());    _chromiumShareContext->makeCurrent();    qt_gl_set_global_share_context(_chromiumShareContext->getContext());     _offscreenContext = new OffscreenGLCanvas();    _offscreenContext->create(_glWidget->context()->contextHandle());    _offscreenContext->makeCurrent();    initializeGL();    _offscreenContext->makeCurrent();    // Make sure we don't time out during slow operations at startup    updateHeartbeat();      // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.    // The value will be 0 if the user blew away settings this session, which is both a feature and a bug.    auto gpuIdent = GPUIdent::getInstance();    auto glContextData = getGLContextData();    QJsonObject properties = {        { "version", applicationVersion() },        { "previousSessionCrashed", _previousSessionCrashed },        { "previousSessionRuntime", sessionRunTime.get() },        { "cpu_architecture", QSysInfo::currentCpuArchitecture() },        { "kernel_type", QSysInfo::kernelType() },        { "kernel_version", QSysInfo::kernelVersion() },        { "os_type", QSysInfo::productType() },        { "os_version", QSysInfo::productVersion() },        { "gpu_name", gpuIdent->getName() },        { "gpu_driver", gpuIdent->getDriver() },        { "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) },        { "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) },        { "gl_version", glContextData["version"] },        { "gl_vender", glContextData["vendor"] },        { "gl_sl_version", glContextData["slVersion"] },        { "gl_renderer", glContextData["renderer"] }    };    auto macVersion = QSysInfo::macVersion();    if (macVersion != QSysInfo::MV_None) {        properties["os_osx_version"] = QSysInfo::macVersion();    }    auto windowsVersion = QSysInfo::windowsVersion();    if (windowsVersion != QSysInfo::WV_None) {        properties["os_win_version"] = QSysInfo::windowsVersion();    }    UserActivityLogger::getInstance().logAction("launch", properties);      // Tell our entity edit sender about our known jurisdictions    _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);    _entityEditSender.setMyAvatar(getMyAvatar());     // For now we're going to set the PPS for outbound packets to be super high, this is    // probably not the right long term solution. But for now, we're going to do this to    // allow you to move an entity around in your hand    _entityEditSender.setPacketsPerSecond(3000); // super high!!     _overlays.init(); // do this before scripts load    // Make sure we don't time out during slow operations at startup    updateHeartbeat();     connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));     // hook up bandwidth estimator    QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();    connect(nodeList.data(), &LimitedNodeList::dataSent,        bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData);    connect(&nodeList->getPacketReceiver(), &PacketReceiver::dataReceived,        bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData);     // FIXME -- I'm a little concerned about this.    connect(getMyAvatar()->getSkeletonModel().get(), &SkeletonModel::skeletonLoaded,        this, &Application::checkSkeleton, Qt::QueuedConnection);     // Setup the userInputMapper with the actions    auto userInputMapper = DependencyManager::get<UserInputMapper>();    connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) {        using namespace controller;        auto offscreenUi = DependencyManager::get<OffscreenUi>();        if (offscreenUi->navigationFocused()) {            auto actionEnum = static_cast<Action>(action);            int key = Qt::Key_unknown;            static int lastKey = Qt::Key_unknown;            bool navAxis = false;            switch (actionEnum) {                case Action::UI_NAV_VERTICAL:                    navAxis = true;                    if (state > 0.0f) {                        key = Qt::Key_Up;                    } else if (state < 0.0f) {                        key = Qt::Key_Down;                    }                    break;                 case Action::UI_NAV_LATERAL:                    navAxis = true;                    if (state > 0.0f) {                        key = Qt::Key_Right;                    } else if (state < 0.0f) {                        key = Qt::Key_Left;                    }                    break;                 case Action::UI_NAV_GROUP:                    navAxis = true;                    if (state > 0.0f) {                        key = Qt::Key_Tab;                    } else if (state < 0.0f) {                        key = Qt::Key_Backtab;                    }                    break;                 case Action::UI_NAV_BACK:                    key = Qt::Key_Escape;                    break;                 case Action::UI_NAV_SELECT:                    key = Qt::Key_Return;                    break;                default:                    break;            }             if (navAxis) {                if (lastKey != Qt::Key_unknown) {                    QKeyEvent event(QEvent::KeyRelease, lastKey, Qt::NoModifier);                    sendEvent(offscreenUi->getWindow(), &event);                    lastKey = Qt::Key_unknown;                }                 if (key != Qt::Key_unknown) {                    QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier);                    sendEvent(offscreenUi->getWindow(), &event);                    lastKey = key;                }            } else if (key != Qt::Key_unknown) {                if (state) {                    QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier);                    sendEvent(offscreenUi->getWindow(), &event);                } else {                    QKeyEvent event(QEvent::KeyRelease, key, Qt::NoModifier);                    sendEvent(offscreenUi->getWindow(), &event);                }                return;            }        }         if (action == controller::toInt(controller::Action::RETICLE_CLICK)) {            auto reticlePos = getApplicationCompositor().getReticlePosition();            QPoint localPos(reticlePos.x, reticlePos.y); // both hmd and desktop already handle this in our coordinates.            if (state) {                QMouseEvent mousePress(QEvent::MouseButtonPress, localPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);                sendEvent(_glWidget, &mousePress);                _reticleClickPressed = true;            } else {                QMouseEvent mouseRelease(QEvent::MouseButtonRelease, localPos, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);                sendEvent(_glWidget, &mouseRelease);                _reticleClickPressed = false;            }            return; // nothing else to do        }         if (state) {            if (action == controller::toInt(controller::Action::TOGGLE_MUTE)) {                DependencyManager::get<AudioClient>()->toggleMute();            } else if (action == controller::toInt(controller::Action::CYCLE_CAMERA)) {                cycleCamera();            } else if (action == controller::toInt(controller::Action::UI_NAV_SELECT)) {                if (!offscreenUi->navigationFocused()) {                    toggleMenuUnderReticle();                }            } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {                toggleMenuUnderReticle();            } else if (action == controller::toInt(controller::Action::RETICLE_X)) {                auto oldPos = getApplicationCompositor().getReticlePosition();                getApplicationCompositor().setReticlePosition({ oldPos.x + state, oldPos.y });            } else if (action == controller::toInt(controller::Action::RETICLE_Y)) {                auto oldPos = getApplicationCompositor().getReticlePosition();                getApplicationCompositor().setReticlePosition({ oldPos.x, oldPos.y + state });            } else if (action == controller::toInt(controller::Action::TOGGLE_OVERLAY)) {                toggleOverlays();            }        }    });     _applicationStateDevice = userInputMapper->getStateDevice();     _applicationStateDevice->setInputVariant(STATE_IN_HMD, []() -> float {        return qApp->isHMDMode() ? 1 : 0;    });    _applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float {        return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0;    });    _applicationStateDevice->setInputVariant(STATE_GROUNDED, []() -> float {        return qApp->getMyAvatar()->getCharacterController()->onGround() ? 1 : 0;    });    _applicationStateDevice->setInputVariant(STATE_NAV_FOCUSED, []() -> float {        return DependencyManager::get<OffscreenUi>()->navigationFocused() ? 1 : 0;    });     // Setup the keyboardMouseDevice and the user input mapper with the default bindings    userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice());     // force the model the look at the correct directory (weird order of operations issue)    scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation());    // do this as late as possible so that all required subsystems are initialized    scriptEngines->loadScripts();    // Make sure we don't time out during slow operations at startup    updateHeartbeat();     loadSettings();    // Make sure we don't time out during slow operations at startup    updateHeartbeat();     int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now    connect(&_settingsTimer, &QTimer::timeout, this, &Application::saveSettings);    connect(&_settingsThread, SIGNAL(started()), &_settingsTimer, SLOT(start()));    connect(&_settingsThread, SIGNAL(finished()), &_settingsTimer, SLOT(stop()));    _settingsTimer.moveToThread(&_settingsThread);    _settingsTimer.setSingleShot(false);    _settingsTimer.setInterval(SAVE_SETTINGS_INTERVAL);    _settingsThread.start();     if (Menu::getInstance()->isOptionChecked(MenuOption::FirstPerson)) {        getMyAvatar()->setBoomLength(MyAvatar::ZOOM_MIN);  // So that camera doesn't auto-switch to third person.    } else if (Menu::getInstance()->isOptionChecked(MenuOption::IndependentMode)) {        Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);        cameraMenuChanged();    } else if (Menu::getInstance()->isOptionChecked(MenuOption::CameraEntityMode)) {        Menu::getInstance()->setIsOptionChecked(MenuOption::ThirdPerson, true);        cameraMenuChanged();    }     // set the local loopback interface for local sounds from audio scripts    AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data());     this->installEventFilter(this);     // initialize our face trackers after loading the menu settings    auto faceshiftTracker = DependencyManager::get<Faceshift>();    faceshiftTracker->init();    connect(faceshiftTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled);#ifdef HAVE_DDE    auto ddeTracker = DependencyManager::get<DdeFaceTracker>();    ddeTracker->init();

Lines 501–1000 of 5476. Previous lines