Showing the changes to interface/src/Application.cpp (1 of 37 files). Show all
Unified · Split
| @@ -68,6 +68,7 @@ · 67 unmodified lines | ||
| 68 | 68 | #include <input-plugins/InputPlugin.h> |
| 69 | 69 | #include <controllers/UserInputMapper.h> |
| 70 | 70 | #include <controllers/StateController.h> |
| 71 | +#include <UserActivityLoggerScriptingInterface.h> | |
| 71 | 72 | #include <LogHandler.h> |
| 72 | 73 | #include <MainWindow.h> |
| 73 | 74 | #include <MessagesClient.h> |
| @@ -150,6 +151,8 @@ · 76 unmodified lines | ||
| 150 | 151 | #include "InterfaceParentFinder.h" |
| 151 | 152 | |
| 152 | 153 | #include "FrameTimingsScriptingInterface.h" |
| 154 | +#include <GPUIdent.h> | |
| 155 | +#include <gl/GLHelpers.h> | |
| 153 | 156 | |
| 154 | 157 | // On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU |
| 155 | 158 | // FIXME seems to be broken. |
| @@ -437,7 +440,7 @@ bool setupEssentials(int& argc, char** argv) { · 281 unmodified lines | ||
| 437 | 440 | DependencyManager::set<HMDScriptingInterface>(); |
| 438 | 441 | DependencyManager::set<ResourceScriptingInterface>(); |
| 439 | 442 | DependencyManager::set<ToolbarScriptingInterface>(); |
| 440 | − | |
| 443 | + DependencyManager::set<UserActivityLoggerScriptingInterface>(); | |
| 441 | 444 | |
| 442 | 445 | #if defined(Q_OS_MAC) || defined(Q_OS_WIN) |
| 443 | 446 | DependencyManager::set<SpeechRecognizer>(); |
| @@ -675,10 +678,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 231 unmodified lines | ||
| 675 | 678 | accountManager->setIsAgent(true); |
| 676 | 679 | accountManager->setAuthURL(NetworkingConstants::METAVERSE_SERVER_URL); |
| 677 | 680 | |
| 678 | − // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. | |
| 679 | − // The value will be 0 if the user blew away settings this session, which is both a feature and a bug. | |
| 680 | − UserActivityLogger::getInstance().launch(applicationVersion(), _previousSessionCrashed, sessionRunTime.get()); | |
| 681 | − | |
| 682 | 681 | auto addressManager = DependencyManager::get<AddressManager>(); |
| 683 | 682 | |
| 684 | 683 | // use our MyAvatar position and quat for address manager path |
| @@ -768,6 +767,39 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 83 unmodified lines | ||
| 768 | 767 | // Make sure we don't time out during slow operations at startup |
| 769 | 768 | updateHeartbeat(); |
| 770 | 769 | |
| 770 | + | |
| 771 | + // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. | |
| 772 | + // The value will be 0 if the user blew away settings this session, which is both a feature and a bug. | |
| 773 | + auto gpuIdent = GPUIdent::getInstance(); | |
| 774 | + auto glContextData = getGLContextData(); | |
| 775 | + QJsonObject properties = { | |
| 776 | + { "previousSessionCrashed", _previousSessionCrashed }, | |
| 777 | + { "previousSessionRuntime", sessionRunTime.get() }, | |
| 778 | + { "cpu_architecture", QSysInfo::currentCpuArchitecture() }, | |
| 779 | + { "kernel_type", QSysInfo::kernelType() }, | |
| 780 | + { "kernel_version", QSysInfo::kernelVersion() }, | |
| 781 | + { "os_type", QSysInfo::productType() }, | |
| 782 | + { "os_version", QSysInfo::productVersion() }, | |
| 783 | + { "gpu_name", gpuIdent->getName() }, | |
| 784 | + { "gpu_driver", gpuIdent->getDriver() }, | |
| 785 | + { "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) }, | |
| 786 | + { "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) }, | |
| 787 | + { "gl_version", glContextData["version"] }, | |
| 788 | + { "gl_vender", glContextData["vendor"] }, | |
| 789 | + { "gl_sl_version", glContextData["slVersion"] }, | |
| 790 | + { "gl_renderer", glContextData["renderer"] } | |
| 791 | + }; | |
| 792 | + auto macVersion = QSysInfo::macVersion(); | |
| 793 | + if (macVersion != QSysInfo::MV_None) { | |
| 794 | + properties["os_osx_version"] = QSysInfo::macVersion(); | |
| 795 | + } | |
| 796 | + auto windowsVersion = QSysInfo::windowsVersion(); | |
| 797 | + if (windowsVersion != QSysInfo::WV_None) { | |
| 798 | + properties["os_win_version"] = QSysInfo::windowsVersion(); | |
| 799 | + } | |
| 800 | + UserActivityLogger::getInstance().logAction("launch", properties); | |
| 801 | + | |
| 802 | + | |
| 771 | 803 | // Tell our entity edit sender about our known jurisdictions |
| 772 | 804 | _entityEditSender.setServerJurisdictions(&_entityServerJurisdictions); |
| 773 | 805 | _entityEditSender.setMyAvatar(getMyAvatar()); |
| @@ -1063,6 +1095,89 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : · 289 unmodified lines | ||
| 1063 | 1095 | } |
| 1064 | 1096 | }); |
| 1065 | 1097 | |
| 1098 | + // Add periodic checks to send user activity data | |
| 1099 | + static int CHECK_NEARBY_AVATARS_INTERVAL_MS = 10000; | |
| 1100 | + static int SEND_STATS_INTERVAL_MS = 10000; | |
| 1101 | + static int NEARBY_AVATAR_RADIUS_METERS = 10; | |
| 1102 | + | |
| 1103 | + // Periodically send fps as a user activity event | |
| 1104 | + QTimer* sendStatsTimer = new QTimer(this); | |
| 1105 | + sendStatsTimer->setInterval(SEND_STATS_INTERVAL_MS); | |
| 1106 | + connect(sendStatsTimer, &QTimer::timeout, this, [this]() { | |
| 1107 | + QJsonObject properties = {}; | |
| 1108 | + MemoryInfo memInfo; | |
| 1109 | + if (getMemoryInfo(memInfo)) { | |
| 1110 | + properties["system_memory_total"] = static_cast<qint64>(memInfo.totalMemoryBytes); | |
| 1111 | + properties["system_memory_used"] = static_cast<qint64>(memInfo.usedMemoryBytes); | |
| 1112 | + properties["process_memory_used"] = static_cast<qint64>(memInfo.processUsedMemoryBytes); | |
| 1113 | + } | |
| 1114 | + | |
| 1115 | + auto displayPlugin = qApp->getActiveDisplayPlugin(); | |
| 1116 | + | |
| 1117 | + properties["fps"] = _frameCounter.rate(); | |
| 1118 | + properties["present_rate"] = displayPlugin->presentRate(); | |
| 1119 | + properties["new_frame_present_rate"] = displayPlugin->newFramePresentRate(); | |
| 1120 | + properties["dropped_frame_rate"] = displayPlugin->droppedFrameRate(); | |
| 1121 | + properties["sim_rate"] = getAverageSimsPerSecond(); | |
| 1122 | + properties["avatar_sim_rate"] = getAvatarSimrate(); | |
| 1123 | + | |
| 1124 | + auto bandwidthRecorder = DependencyManager::get<BandwidthRecorder>(); | |
| 1125 | + properties["packet_rate_in"] = bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond(); | |
| 1126 | + properties["packet_rate_out"] = bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond(); | |
| 1127 | + properties["kbps_in"] = bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond(); | |
| 1128 | + properties["kbps_out"] = bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond(); | |
| 1129 | + | |
| 1130 | + auto nodeList = DependencyManager::get<NodeList>(); | |
| 1131 | + SharedNodePointer entityServerNode = nodeList->soloNodeOfType(NodeType::EntityServer); | |
| 1132 | + SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); | |
| 1133 | + SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); | |
| 1134 | + SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer); | |
| 1135 | + SharedNodePointer messagesMixerNode = nodeList->soloNodeOfType(NodeType::MessagesMixer); | |
| 1136 | + properties["entity_ping"] = entityServerNode ? entityServerNode->getPingMs() : -1; | |
| 1137 | + properties["audio_ping"] = audioMixerNode ? audioMixerNode->getPingMs() : -1; | |
| 1138 | + properties["avatar_ping"] = avatarMixerNode ? avatarMixerNode->getPingMs() : -1; | |
| 1139 | + properties["asset_ping"] = assetServerNode ? assetServerNode->getPingMs() : -1; | |
| 1140 | + properties["messages_ping"] = messagesMixerNode ? messagesMixerNode->getPingMs() : -1; | |
| 1141 | + | |
| 1142 | + auto loadingRequests = ResourceCache::getLoadingRequests(); | |
| 1143 | + properties["active_downloads"] = loadingRequests.size(); | |
| 1144 | + properties["pending_downloads"] = ResourceCache::getPendingRequestCount(); | |
| 1145 | + | |
| 1146 | + properties["throttled"] = _displayPlugin ? _displayPlugin->isThrottled() : false; | |
| 1147 | + | |
| 1148 | + UserActivityLogger::getInstance().logAction("stats", properties); | |
| 1149 | + }); | |
| 1150 | + sendStatsTimer->start(); | |
| 1151 | + | |
| 1152 | + | |
| 1153 | + // Periodically check for count of nearby avatars | |
| 1154 | + static int lastCountOfNearbyAvatars = -1; | |
| 1155 | + QTimer* checkNearbyAvatarsTimer = new QTimer(this); | |
| 1156 | + checkNearbyAvatarsTimer->setInterval(CHECK_NEARBY_AVATARS_INTERVAL_MS); | |
| 1157 | + connect(checkNearbyAvatarsTimer, &QTimer::timeout, this, [this]() { | |
| 1158 | + auto avatarManager = DependencyManager::get<AvatarManager>(); | |
| 1159 | + int nearbyAvatars = avatarManager->numberOfAvatarsInRange(avatarManager->getMyAvatar()->getPosition(), | |
| 1160 | + NEARBY_AVATAR_RADIUS_METERS) - 1; | |
| 1161 | + if (nearbyAvatars != lastCountOfNearbyAvatars) { | |
| 1162 | + lastCountOfNearbyAvatars = nearbyAvatars; | |
| 1163 | + UserActivityLogger::getInstance().logAction("nearby_avatars", { { "count", nearbyAvatars } }); | |
| 1164 | + } | |
| 1165 | + }); | |
| 1166 | + checkNearbyAvatarsTimer->start(); | |
| 1167 | + | |
| 1168 | + // Track user activity event when we receive a mute packet | |
| 1169 | + auto onMutedByMixer = []() { | |
| 1170 | + UserActivityLogger::getInstance().logAction("received_mute_packet"); | |
| 1171 | + }; | |
| 1172 | + connect(DependencyManager::get<AudioClient>().data(), &AudioClient::mutedByMixer, this, onMutedByMixer); | |
| 1173 | + | |
| 1174 | + // Track when the address bar is opened | |
| 1175 | + auto onAddressBarToggled = [this]() { | |
| 1176 | + // Record time | |
| 1177 | + UserActivityLogger::getInstance().logAction("opened_address_bar", { { "uptime_ms", _sessionRunTimer.elapsed() } }); | |
| 1178 | + }; | |
| 1179 | + connect(DependencyManager::get<DialogsManager>().data(), &DialogsManager::addressBarToggled, this, onAddressBarToggled); | |
| 1180 | + | |
| 1066 | 1181 | // Make sure we don't time out during slow operations at startup |
| 1067 | 1182 | updateHeartbeat(); |
| 1068 | 1183 | |
| @@ -4578,6 +4693,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri · 3509 unmodified lines | ||
| 4578 | 4693 | |
| 4579 | 4694 | scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data()); |
| 4580 | 4695 | scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface()); |
| 4696 | + | |
| 4697 | + scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data()); | |
| 4581 | 4698 | } |
| 4582 | 4699 | |
| 4583 | 4700 | bool Application::canAcceptURL(const QString& urlString) const { |
| @@ -5180,6 +5297,11 @@ void Application::updateDisplayMode() { · 596 unmodified lines | ||
| 5180 | 5297 | return; |
| 5181 | 5298 | } |
| 5182 | 5299 | |
| 5300 | + UserActivityLogger::getInstance().logAction("changed_display_mode", { | |
| 5301 | + { "previous_display_mode", _displayPlugin ? _displayPlugin->getName() : "" }, | |
| 5302 | + { "display_mode", newDisplayPlugin ? newDisplayPlugin->getName() : "" } | |
| 5303 | + }); | |
| 5304 | + | |
| 5183 | 5305 | auto offscreenUi = DependencyManager::get<OffscreenUi>(); |
| 5184 | 5306 | |
| 5185 | 5307 | // Make the switch atomic from the perspective of other threads |