diff --git a/CMakeLists.txt b/CMakeLists.txt index b2f35b1443..b91fac2538 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,26 +111,29 @@ get_filename_component(QT_DIR "${QT_CMAKE_PREFIX_PATH}/../../" ABSOLUTE) set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH}) if (APPLE) + + SET(OSX_SDK "10.9" CACHE String "OS X SDK version to look for inside Xcode bundle or at OSX_SDK_PATH") + # set our OS X deployment target set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8) - # find the 10.9 SDK path + # find the SDK path for the desired SDK find_path( _OSX_DESIRED_SDK_PATH - NAME MacOSX10.9.sdk + NAME MacOSX${OSX_SDK}.sdk HINTS ${OSX_SDK_PATH} PATHS /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/ ) if (NOT _OSX_DESIRED_SDK_PATH) - message(FATAL_ERROR "Could not find OS X 10.9 SDK. Please pass OSX_SDK_PATH to CMake to point us to your SDKs directory.") + message(FATAL_ERROR "Could not find OS X ${OSX_SDK} SDK. Please pass OSX_SDK_PATH to CMake to point us to your SDKs directory.") else () - message(STATUS "Found OS X 10.9 SDK at ${_OSX_DESIRED_SDK_PATH}/MacOSX10.9.sdk") + message(STATUS "Found OS X ${OSX_SDK} SDK at ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk") endif () # set that as the SDK to use - set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX10.9.sdk) + set(CMAKE_OSX_SYSROOT ${_OSX_DESIRED_SDK_PATH}/MacOSX${OSX_SDK}.sdk) endif () # Hide automoc folders (for IDEs) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 5bb31c8c6f..58cf3c49f3 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -95,8 +94,10 @@ void AssignmentClientMonitor::stopChildProcesses() { // ask child processes to terminate foreach(QProcess* childProcess, _childProcesses) { - qDebug() << "Attempting to terminate child process" << childProcess->processId(); - childProcess->terminate(); + if (childProcess->processId() > 0) { + qDebug() << "Attempting to terminate child process" << childProcess->processId(); + childProcess->terminate(); + } } simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); @@ -104,8 +105,10 @@ void AssignmentClientMonitor::stopChildProcesses() { if (_childProcesses.size() > 0) { // ask even more firmly foreach(QProcess* childProcess, _childProcesses) { - qDebug() << "Attempting to kill child process" << childProcess->processId(); - childProcess->kill(); + if (childProcess->processId() > 0) { + qDebug() << "Attempting to kill child process" << childProcess->processId(); + childProcess->kill(); + } } simultaneousWaitOnChildren(WAIT_FOR_CHILD_MSECS); @@ -155,12 +158,14 @@ void AssignmentClientMonitor::spawnChildClient() { assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels); assignmentClient->start(QCoreApplication::applicationFilePath(), _childArguments); - - // make sure we hear that this process has finished when it does - connect(assignmentClient, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(childProcessFinished())); - - qDebug() << "Spawned a child client with PID" << assignmentClient->pid(); - _childProcesses.insert(assignmentClient->processId(), assignmentClient); + + if (assignmentClient->processId() > 0) { + // make sure we hear that this process has finished when it does + connect(assignmentClient, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(childProcessFinished())); + + qDebug() << "Spawned a child client with PID" << assignmentClient->processId(); + _childProcesses.insert(assignmentClient->processId(), assignmentClient); + } } void AssignmentClientMonitor::checkSpares() { diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 9d60b74508..138adb4716 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -12,14 +12,12 @@ #include "AssetServer.h" -#include +#include #include +#include #include #include #include -#include -#include -#include #include #include "NetworkLogging.h" @@ -165,3 +163,61 @@ void AssetServer::handleAssetUpload(QSharedPointer packetList, Sha } } +void AssetServer::sendStatsPacket() { + QJsonObject serverStats; + + auto stats = DependencyManager::get()->sampleStatsForAllConnections(); + + for (const auto& stat : stats) { + QJsonObject nodeStats; + auto endTimeMs = std::chrono::duration_cast(stat.second.endTime); + QDateTime date = QDateTime::fromMSecsSinceEpoch(endTimeMs.count()); + + QJsonObject connectionStats; + connectionStats["lastHeard"] = date.toString(); + connectionStats["estimatedBandwith"] = stat.second.estimatedBandwith; + connectionStats["rtt"] = stat.second.rtt; + connectionStats["congestionWindowSize"] = stat.second.congestionWindowSize; + connectionStats["packetSendPeriod"] = stat.second.packetSendPeriod; + nodeStats["connection"] = connectionStats; + + QJsonObject sendingStats; + sendingStats["sendRate"] = stat.second.sendRate; + sendingStats["sentPackets"] = stat.second.sentPackets; + sendingStats["receivedACK"] = stat.second.events[udt::ConnectionStats::Stats::ReceivedACK]; + sendingStats["processedACK"] = stat.second.events[udt::ConnectionStats::Stats::ProcessedACK]; + sendingStats["receivedLightACK"] = stat.second.events[udt::ConnectionStats::Stats::ReceivedLightACK]; + sendingStats["receivedNAK"] = stat.second.events[udt::ConnectionStats::Stats::ReceivedNAK]; + sendingStats["receivedTimeoutNAK"] = stat.second.events[udt::ConnectionStats::Stats::ReceivedTimeoutNAK]; + sendingStats["sentACK2"] = stat.second.events[udt::ConnectionStats::Stats::SentACK2]; + sendingStats["retransmission"] = stat.second.events[udt::ConnectionStats::Stats::Retransmission]; + nodeStats["sending"] = sendingStats; + + QJsonObject receivingStats; + receivingStats["receiveRate"] = stat.second.receiveRate; + receivingStats["receivedPackets"] = stat.second.receivedPackets; + receivingStats["SentACK"] = stat.second.events[udt::ConnectionStats::Stats::SentACK]; + receivingStats["SentLightACK"] = stat.second.events[udt::ConnectionStats::Stats::SentLightACK]; + receivingStats["SentNAK"] = stat.second.events[udt::ConnectionStats::Stats::SentNAK]; + receivingStats["SentTimeoutNAK"] = stat.second.events[udt::ConnectionStats::Stats::SentTimeoutNAK]; + receivingStats["ReceivedACK2"] = stat.second.events[udt::ConnectionStats::Stats::ReceivedACK2]; + receivingStats["Duplicate"] = stat.second.events[udt::ConnectionStats::Stats::Duplicate]; + nodeStats["receiving"] = receivingStats; + + QString uuid; + auto nodelist = DependencyManager::get(); + if (stat.first == nodelist->getDomainHandler().getSockAddr()) { + uuid = uuidStringWithoutCurlyBraces(nodelist->getDomainHandler().getUUID()); + nodeStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = "DomainServer"; + } else { + auto node = nodelist->findNodeWithAddr(stat.first); + uuid = uuidStringWithoutCurlyBraces(node ? node->getUUID() : QUuid()); + nodeStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuid; + } + + serverStats[uuid] = nodeStats; + } + + // send off the stats packets + ThreadedAssignment::addPacketStatsAndSendStatsPacket(serverStats); +} diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h index 1975f746a9..09144660ec 100644 --- a/assignment-client/src/assets/AssetServer.h +++ b/assignment-client/src/assets/AssetServer.h @@ -31,7 +31,9 @@ private slots: void handleAssetGetInfo(QSharedPointer packet, SharedNodePointer senderNode); void handleAssetGet(QSharedPointer packet, SharedNodePointer senderNode); void handleAssetUpload(QSharedPointer packetList, SharedNodePointer senderNode); - + + void sendStatsPacket(); + private: static void writeError(NLPacketList* packetList, AssetServerError error); QDir _resourcesDirectory; diff --git a/assignment-client/src/assets/SendAssetTask.cpp b/assignment-client/src/assets/SendAssetTask.cpp index f12603d6c7..9211aa1256 100644 --- a/assignment-client/src/assets/SendAssetTask.cpp +++ b/assignment-client/src/assets/SendAssetTask.cpp @@ -52,7 +52,7 @@ void SendAssetTask::run() { qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from " << start << " to " << end; qDebug() << "Starting task to send asset: " << hexHash << " for messageID " << messageID; - auto replyPacketList = std::unique_ptr(new NLPacketList(PacketType::AssetGetReply, QByteArray(), true, true)); + auto replyPacketList = NLPacketList::create(PacketType::AssetGetReply, QByteArray(), true, true); replyPacketList->write(assetHash); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 195bd53a97..66b924b8ab 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -219,7 +219,7 @@ void AvatarMixer::broadcastAvatarData() { } // setup a PacketList for the avatarPackets - NLPacketList avatarPacketList(PacketType::BulkAvatarData); + auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); // this is an AGENT we have received head data from // send back a packet with other active node data to this node @@ -292,13 +292,13 @@ void AvatarMixer::broadcastAvatarData() { otherNodeData->getLastReceivedSequenceNumber()); // start a new segment in the PacketList for this avatar - avatarPacketList.startSegment(); + avatarPacketList->startSegment(); - numAvatarDataBytes += avatarPacketList.write(otherNode->getUUID().toRfc4122()); + numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); numAvatarDataBytes += - avatarPacketList.write(otherAvatar.toByteArray(false, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO)); + avatarPacketList->write(otherAvatar.toByteArray(false, randFloat() < AVATAR_SEND_FULL_UPDATE_RATIO)); - avatarPacketList.endSegment(); + avatarPacketList->endSegment(); // if the receiving avatar has just connected make sure we send out the mesh and billboard // for this avatar (assuming they exist) @@ -344,10 +344,10 @@ void AvatarMixer::broadcastAvatarData() { }); // close the current packet so that we're always sending something - avatarPacketList.closeCurrentPacket(true); + avatarPacketList->closeCurrentPacket(true); // send the avatar data PacketList - nodeList->sendPacketList(avatarPacketList, *node); + nodeList->sendPacketList(std::move(avatarPacketList), *node); // record the bytes sent for other avatar data in the AvatarMixerClientData nodeData->recordSentAvatarData(numAvatarDataBytes); diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index a023b9a7fd..4fde1970c7 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -238,7 +238,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() { return 0; } - NLPacketList nackPacketList(_myServer->getMyEditNackType()); + auto nackPacketList = NLPacketList::create(_myServer->getMyEditNackType()); auto nodeList = DependencyManager::get(); int packetsSent = 0; @@ -274,18 +274,18 @@ int OctreeInboundPacketProcessor::sendNackPackets() { while (it != missingSequenceNumbers.constEnd()) { unsigned short int sequenceNumber = *it; - nackPacketList.writePrimitive(sequenceNumber); + nackPacketList->writePrimitive(sequenceNumber); ++it; } - if (nackPacketList.getNumPackets()) { + if (nackPacketList->getNumPackets()) { qDebug() << "NACK Sent back to editor/client... destinationNode=" << nodeUUID; - packetsSent += nackPacketList.getNumPackets(); + packetsSent += nackPacketList->getNumPackets(); // send the list of nack packets - nodeList->sendPacketList(nackPacketList, *destinationNode); + nodeList->sendPacketList(std::move(nackPacketList), *destinationNode); } ++i; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index a0350622db..e0d0f9eeac 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1293,81 +1293,85 @@ QString OctreeServer::getStatusLink() { } void OctreeServer::sendStatsPacket() { - // TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and - // send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the - // the following features: - // 1) remember last state sent - // 2) only send new data - // 3) automatically break up into multiple packets - static QJsonObject statsObject1; - - QString baseName = getMyServerName() + QString("Server"); - - statsObject1[baseName + QString(".0.1.configuration")] = getConfiguration(); - - statsObject1[baseName + QString(".0.2.detailed_stats_url")] = getStatusLink(); - - statsObject1[baseName + QString(".0.3.uptime")] = getUptime(); - statsObject1[baseName + QString(".0.4.persistFileLoadTime")] = getFileLoadTime(); - statsObject1[baseName + QString(".0.5.clients")] = getCurrentClientCount(); - + // Stats Array 1 + QJsonArray threadsStats; quint64 oneSecondAgo = usecTimestampNow() - USECS_PER_SECOND; + threadsStats.push_back(QJsonObject({{"processing", (double)howManyThreadsDidProcess(oneSecondAgo)}})); + threadsStats.push_back(QJsonObject({{"packetDistributor", (double)howManyThreadsDidPacketDistributor(oneSecondAgo)}})); + threadsStats.push_back(QJsonObject({{"handlePacektSend", (double)howManyThreadsDidHandlePacketSend(oneSecondAgo)}})); + threadsStats.push_back(QJsonObject({{"writeDatagram", (double)howManyThreadsDidCallWriteDatagram(oneSecondAgo)}})); + + QJsonArray statsArray1; + statsArray1.push_back(QJsonObject({{"configuration", getConfiguration()}})); + statsArray1.push_back(QJsonObject({{"detailed_stats_url", getStatusLink()}})); + statsArray1.push_back(QJsonObject({{"uptime", getUptime()}})); + statsArray1.push_back(QJsonObject({{"persistFileLoadTime", getFileLoadTime()}})); + statsArray1.push_back(QJsonObject({{"clients", getCurrentClientCount()}})); + statsArray1.push_back(QJsonObject({{"threads", threadsStats}})); + + // Octree Stats + QJsonArray octreeStats; + octreeStats.push_back(QJsonObject({{"elementCount", (double)OctreeElement::getNodeCount()}})); + octreeStats.push_back(QJsonObject({{"internalElementCount", (double)OctreeElement::getInternalNodeCount()}})); + octreeStats.push_back(QJsonObject({{"leafElementCount", (double)OctreeElement::getLeafNodeCount()}})); + + // Stats Object 2 + QJsonObject dataObject1; + dataObject1["totalPackets"] = (double)OctreeSendThread::_totalPackets; + dataObject1["totalBytes"] = (double)OctreeSendThread::_totalBytes; + dataObject1["totalBytesWasted"] = (double)OctreeSendThread::_totalWastedBytes; + dataObject1["totalBytesOctalCodes"] = (double)OctreePacketData::getTotalBytesOfOctalCodes(); + dataObject1["totalBytesBitMasks"] = (double)OctreePacketData::getTotalBytesOfBitMasks(); + dataObject1["totalBytesBitMasks"] = (double)OctreePacketData::getTotalBytesOfColor(); - statsObject1[baseName + QString(".0.6.threads.1.processing")] = (double)howManyThreadsDidProcess(oneSecondAgo); - statsObject1[baseName + QString(".0.6.threads.2.packetDistributor")] = - (double)howManyThreadsDidPacketDistributor(oneSecondAgo); - statsObject1[baseName + QString(".0.6.threads.3.handlePacektSend")] = - (double)howManyThreadsDidHandlePacketSend(oneSecondAgo); - statsObject1[baseName + QString(".0.6.threads.4.writeDatagram")] = - (double)howManyThreadsDidCallWriteDatagram(oneSecondAgo); - - statsObject1[baseName + QString(".1.1.octree.elementCount")] = (double)OctreeElement::getNodeCount(); - statsObject1[baseName + QString(".1.2.octree.internalElementCount")] = (double)OctreeElement::getInternalNodeCount(); - statsObject1[baseName + QString(".1.3.octree.leafElementCount")] = (double)OctreeElement::getLeafNodeCount(); - - ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject1); - - static QJsonObject statsObject2; - - statsObject2[baseName + QString(".2.outbound.data.totalPackets")] = (double)OctreeSendThread::_totalPackets; - statsObject2[baseName + QString(".2.outbound.data.totalBytes")] = (double)OctreeSendThread::_totalBytes; - statsObject2[baseName + QString(".2.outbound.data.totalBytesWasted")] = (double)OctreeSendThread::_totalWastedBytes; - statsObject2[baseName + QString(".2.outbound.data.totalBytesOctalCodes")] = - (double)OctreePacketData::getTotalBytesOfOctalCodes(); - statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] = - (double)OctreePacketData::getTotalBytesOfBitMasks(); - statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] = (double)OctreePacketData::getTotalBytesOfColor(); - - statsObject2[baseName + QString(".2.outbound.timing.1.avgLoopTime")] = getAverageLoopTime(); - statsObject2[baseName + QString(".2.outbound.timing.2.avgInsideTime")] = getAverageInsideTime(); - statsObject2[baseName + QString(".2.outbound.timing.3.avgTreeLockTime")] = getAverageTreeWaitTime(); - statsObject2[baseName + QString(".2.outbound.timing.4.avgEncodeTime")] = getAverageEncodeTime(); - statsObject2[baseName + QString(".2.outbound.timing.5.avgCompressAndWriteTime")] = getAverageCompressAndWriteTime(); - statsObject2[baseName + QString(".2.outbound.timing.5.avgSendTime")] = getAveragePacketSendingTime(); - statsObject2[baseName + QString(".2.outbound.timing.5.nodeWaitTime")] = getAverageNodeWaitTime(); - - DependencyManager::get()->sendStatsToDomainServer(statsObject2); - - static QJsonObject statsObject3; - - statsObject3[baseName + QString(".3.inbound.data.1.packetQueue")] = - (double)_octreeInboundPacketProcessor->packetsToProcessCount(); - statsObject3[baseName + QString(".3.inbound.data.1.totalPackets")] = - (double)_octreeInboundPacketProcessor->getTotalPacketsProcessed(); - statsObject3[baseName + QString(".3.inbound.data.2.totalElements")] = - (double)_octreeInboundPacketProcessor->getTotalElementsProcessed(); - statsObject3[baseName + QString(".3.inbound.timing.1.avgTransitTimePerPacket")] = - (double)_octreeInboundPacketProcessor->getAverageTransitTimePerPacket(); - statsObject3[baseName + QString(".3.inbound.timing.2.avgProcessTimePerPacket")] = - (double)_octreeInboundPacketProcessor->getAverageProcessTimePerPacket(); - statsObject3[baseName + QString(".3.inbound.timing.3.avgLockWaitTimePerPacket")] = - (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket(); - statsObject3[baseName + QString(".3.inbound.timing.4.avgProcessTimePerElement")] = - (double)_octreeInboundPacketProcessor->getAverageProcessTimePerElement(); - statsObject3[baseName + QString(".3.inbound.timing.5.avgLockWaitTimePerElement")] = - (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement(); - - DependencyManager::get()->sendStatsToDomainServer(statsObject3); + QJsonArray timingArray1; + timingArray1.push_back(QJsonObject({{"avgLoopTime", getAverageLoopTime()}})); + timingArray1.push_back(QJsonObject({{"avgInsideTime", getAverageInsideTime()}})); + timingArray1.push_back(QJsonObject({{"avgTreeLockTime", getAverageTreeWaitTime()}})); + timingArray1.push_back(QJsonObject({{"avgEncodeTime", getAverageEncodeTime()}})); + timingArray1.push_back(QJsonObject({{"avgCompressAndWriteTime", getAverageCompressAndWriteTime()}})); + timingArray1.push_back(QJsonObject({{"avgSendTime", getAveragePacketSendingTime()}})); + timingArray1.push_back(QJsonObject({{"nodeWaitTime", getAverageNodeWaitTime()}})); + + QJsonObject statsObject2; + statsObject2["data"] = dataObject1; + statsObject2["timing"] = timingArray1; + + // Stats Object 3 + QJsonArray dataArray2; + dataArray2.push_back(QJsonObject({{"packetQueue", + (double)_octreeInboundPacketProcessor->packetsToProcessCount()}})); + dataArray2.push_back(QJsonObject({{"totalPackets", + (double)_octreeInboundPacketProcessor->getTotalPacketsProcessed()}})); + dataArray2.push_back(QJsonObject({{"totalElements", + (double)_octreeInboundPacketProcessor->getTotalElementsProcessed()}})); + + QJsonArray timingArray2; + timingArray2.push_back(QJsonObject({{"avgTransitTimePerPacket", + (double)_octreeInboundPacketProcessor->getAverageTransitTimePerPacket()}})); + timingArray2.push_back(QJsonObject({{"avgProcessTimePerPacket", + (double)_octreeInboundPacketProcessor->getAverageProcessTimePerPacket()}})); + timingArray2.push_back(QJsonObject({{"avgLockWaitTimePerPacket", + (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket()}})); + timingArray2.push_back(QJsonObject({{"avgProcessTimePerElement", + (double)_octreeInboundPacketProcessor->getAverageProcessTimePerElement()}})); + timingArray2.push_back(QJsonObject({{"avgLockWaitTimePerElement", + (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement()}})); + + QJsonObject statsObject3; + statsObject3["data"] = dataArray2; + statsObject3["timing"] = timingArray2; + + // Merge everything + QJsonArray jsonArray; + jsonArray.push_back(statsArray1); + jsonArray.push_back(QJsonObject({{"octree", octreeStats}})); + jsonArray.push_back(QJsonObject({{"outbound", statsObject2}})); + jsonArray.push_back(QJsonObject({{"inbound", statsObject3}})); + + QJsonObject statsObject; + statsObject[QString(getMyServerName()) + "Server"] = jsonArray; + addPacketStatsAndSendStatsPacket(statsObject); } QMap OctreeServer::_threadsDidProcess; diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index ae827f3a4a..90b22ffdd8 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -17,7 +17,6 @@ #include #include -#include #include "DomainServer.h" #include "DomainServerNodeData.h" @@ -272,9 +271,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect // if we have a username from the connect request, set it on the DomainServerNodeData nodeData->setUsername(username); - // also add an interpolation to JSONBreakableMarshal so that servers can get username in stats - JSONBreakableMarshal::addInterpolationForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY, - uuidStringWithoutCurlyBraces(newNode->getUUID()), username); + // also add an interpolation to DomainServerNodeData so that servers can get username in stats + nodeData->addOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY, + uuidStringWithoutCurlyBraces(newNode->getUUID()), username); return newNode; } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 2b59ba7510..49b7f2e183 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -287,7 +286,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { packetReceiver.registerListener(PacketType::RequestAssignment, this, "processRequestAssignmentPacket"); packetReceiver.registerListener(PacketType::DomainListRequest, this, "processListRequestPacket"); packetReceiver.registerListener(PacketType::DomainServerPathQuery, this, "processPathQueryPacket"); - packetReceiver.registerListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket"); + packetReceiver.registerMessageListener(PacketType::NodeJsonStats, this, "processNodeJSONStatsPacket"); // NodeList won't be available to the settings manager when it is created, so call registerListener here packetReceiver.registerListener(PacketType::DomainSettingsRequest, &_settingsManager, "processSettingsRequestPacket"); @@ -679,10 +678,10 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif extendedHeaderStream << (quint8) node->getCanAdjustLocks(); extendedHeaderStream << (quint8) node->getCanRez(); - NLPacketList domainListPackets(PacketType::DomainList, extendedHeader); + auto domainListPackets = NLPacketList::create(PacketType::DomainList, extendedHeader); // always send the node their own UUID back - QDataStream domainListStream(&domainListPackets); + QDataStream domainListStream(domainListPackets.get()); DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); @@ -698,7 +697,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) { // since we're about to add a node to the packet we start a segment - domainListPackets.startSegment(); + domainListPackets->startSegment(); // don't send avatar nodes to other avatars, that will come from avatar mixer domainListStream << *otherNode.data(); @@ -707,17 +706,17 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif domainListStream << connectionSecretForNodes(node, otherNode); // we've added the node we wanted so end the segment now - domainListPackets.endSegment(); + domainListPackets->endSegment(); } }); } } // send an empty list to the node, in case there were no other nodes - domainListPackets.closeCurrentPacket(true); + domainListPackets->closeCurrentPacket(true); // write the PacketList to this node - limitedNodeList->sendPacketList(domainListPackets, *node); + limitedNodeList->sendPacketList(std::move(domainListPackets), *node); } QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) { @@ -1007,10 +1006,10 @@ void DomainServer::sendHeartbeatToIceServer() { DependencyManager::get()->sendHeartbeatToIceServer(_iceServerSocket); } -void DomainServer::processNodeJSONStatsPacket(QSharedPointer packet, SharedNodePointer sendingNode) { +void DomainServer::processNodeJSONStatsPacket(QSharedPointer packetList, SharedNodePointer sendingNode) { auto nodeData = dynamic_cast(sendingNode->getLinkedData()); if (nodeData) { - nodeData->processJSONStatsPacket(*packet); + nodeData->updateJSONStats(packetList->getMessage()); } } @@ -1676,9 +1675,9 @@ void DomainServer::nodeKilled(SharedNodePointer node) { } } - // If this node was an Agent ask JSONBreakableMarshal to potentially remove the interpolation we stored - JSONBreakableMarshal::removeInterpolationForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY, - uuidStringWithoutCurlyBraces(node->getUUID())); + // If this node was an Agent ask DomainServerNodeData to potentially remove the interpolation we stored + nodeData->removeOverrideForKey(USERNAME_UUID_REPLACEMENT_STATS_KEY, + uuidStringWithoutCurlyBraces(node->getUUID())); // cleanup the connection secrets that we set up for this node (on the other nodes) foreach (const QUuid& otherNodeSessionUUID, nodeData->getSessionSecretHash().keys()) { diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index a66192b44e..df42bf3ad9 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -58,7 +58,7 @@ public slots: void processRequestAssignmentPacket(QSharedPointer packet); void processListRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); - void processNodeJSONStatsPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void processNodeJSONStatsPacket(QSharedPointer packetList, SharedNodePointer sendingNode); void processPathQueryPacket(QSharedPointer packet); private slots: diff --git a/domain-server/src/DomainServerNodeData.cpp b/domain-server/src/DomainServerNodeData.cpp index a66eec5705..974d4a59c3 100644 --- a/domain-server/src/DomainServerNodeData.cpp +++ b/domain-server/src/DomainServerNodeData.cpp @@ -10,40 +10,69 @@ // #include +#include +#include #include #include -#include #include #include "DomainServerNodeData.h" -DomainServerNodeData::DomainServerNodeData() : - _sessionSecretHash(), - _assignmentUUID(), - _walletUUID(), - _username(), - _paymentIntervalTimer(), - _statsJSONObject(), - _sendingSockAddr(), - _isAuthenticated(true) -{ +DomainServerNodeData::StringPairHash DomainServerNodeData::_overrideHash; + +DomainServerNodeData::DomainServerNodeData() { _paymentIntervalTimer.start(); } -void DomainServerNodeData::processJSONStatsPacket(NLPacket& statsPacket) { - QVariantMap packetVariantMap = JSONBreakableMarshal::fromStringBuffer(statsPacket.readAll()); - _statsJSONObject = mergeJSONStatsFromNewObject(QJsonObject::fromVariantMap(packetVariantMap), _statsJSONObject); +void DomainServerNodeData::updateJSONStats(QByteArray statsByteArray) { + auto document = QJsonDocument::fromBinaryData(statsByteArray); + Q_ASSERT(document.isObject()); + _statsJSONObject = overrideValuesIfNeeded(document.object()); } -QJsonObject DomainServerNodeData::mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject) { - foreach(const QString& key, newObject.keys()) { - if (newObject[key].isObject() && destinationObject.contains(key)) { - destinationObject[key] = mergeJSONStatsFromNewObject(newObject[key].toObject(), destinationObject[key].toObject()); +QJsonObject DomainServerNodeData::overrideValuesIfNeeded(const QJsonObject& newStats) { + QJsonObject result; + for (auto it = newStats.constBegin(); it != newStats.constEnd(); ++it) { + const auto& key = it.key(); + const auto& value = it.value(); + + auto overrideIt = value.isString() ? _overrideHash.find({key, value.toString()}) : _overrideHash.end(); + if (overrideIt != _overrideHash.end()) { + // We have a match, override the value + result[key] = *overrideIt; + } else if (value.isObject()) { + result[key] = overrideValuesIfNeeded(value.toObject()); + } else if (value.isArray()) { + result[key] = overrideValuesIfNeeded(value.toArray()); } else { - destinationObject[key] = newObject[key]; + result[key] = newStats[key]; } } - - return destinationObject; + return result; +} + +QJsonArray DomainServerNodeData::overrideValuesIfNeeded(const QJsonArray& newStats) { + QJsonArray result; + for (const auto& value : newStats) { + if (value.isObject()) { + result.push_back(overrideValuesIfNeeded(value.toObject())); + } else if (value.isArray()) { + result.push_back(overrideValuesIfNeeded(value.toArray())); + } else { + result.push_back(value); + } + } + return result; +} + +void DomainServerNodeData::addOverrideForKey(const QString& key, const QString& value, + const QString& overrideValue) { + // Insert override value + _overrideHash.insert({key, value}, overrideValue); +} + +void DomainServerNodeData::removeOverrideForKey(const QString& key, const QString& value) { + // Remove override value + _overrideHash.remove({key, value}); } diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index 2c57368653..cd68aa3006 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -27,7 +27,7 @@ public: const QJsonObject& getStatsJSONObject() const { return _statsJSONObject; } - void processJSONStatsPacket(NLPacket& packet); + void updateJSONStats(QByteArray statsByteArray); void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } const QUuid& getAssignmentUUID() const { return _assignmentUUID; } @@ -54,17 +54,25 @@ public: void setNodeVersion(const QString& nodeVersion) { _nodeVersion = nodeVersion; } const QString& getNodeVersion() { return _nodeVersion; } + void addOverrideForKey(const QString& key, const QString& value, const QString& overrideValue); + void removeOverrideForKey(const QString& key, const QString& value); + private: - QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject); - + QJsonObject overrideValuesIfNeeded(const QJsonObject& newStats); + QJsonArray overrideValuesIfNeeded(const QJsonArray& newStats); + QHash _sessionSecretHash; QUuid _assignmentUUID; QUuid _walletUUID; QString _username; QElapsedTimer _paymentIntervalTimer; + + using StringPairHash = QHash, QString>; QJsonObject _statsJSONObject; + static StringPairHash _overrideHash; + HifiSockAddr _sendingSockAddr; - bool _isAuthenticated; + bool _isAuthenticated = true; NodeSet _nodeInterestSet; QString _nodeVersion; }; diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index fc0ed95b92..f650089486 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -74,7 +74,7 @@ void DomainServerSettingsManager::processSettingsRequestPacket(QSharedPointer(new NLPacketList(PacketType::DomainSettings, QByteArray(), true, true)); + auto packetList = NLPacketList::create(PacketType::DomainSettings, QByteArray(), true, true); packetList->write(json); diff --git a/examples/edit.js b/examples/edit.js index 55b745a4e1..d778ff324d 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -260,6 +260,7 @@ var toolBar = (function () { cameraManager.disable(); } else { hasShownPropertiesTool = false; + cameraManager.enable(); entityListTool.setVisible(true); gridTool.setVisible(true); grid.setEnabled(true); @@ -669,11 +670,15 @@ function mouseMove(event) { lastMousePosition = { x: event.x, y: event.y }; + highlightEntityUnderCursor(lastMousePosition, false); idleMouseTimerId = Script.setTimeout(handleIdleMouse, IDLE_MOUSE_TIMEOUT); } function handleIdleMouse() { idleMouseTimerId = null; + if (isActive) { + highlightEntityUnderCursor(lastMousePosition, true); + } } function highlightEntityUnderCursor(position, accurateRay) { @@ -797,7 +802,6 @@ function mouseClickEvent(event) { selectionDisplay.select(selectedEntityID, event); if (Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT)) { - cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); @@ -1138,7 +1142,6 @@ Controller.keyReleaseEvent.connect(function (event) { } else if (event.text == "f") { if (isActive) { if (selectionManager.hasSelection()) { - cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); diff --git a/examples/libraries/entityList.js b/examples/libraries/entityList.js index 3d6bf4d14f..66dc9f336f 100644 --- a/examples/libraries/entityList.js +++ b/examples/libraries/entityList.js @@ -49,7 +49,7 @@ EntityListTool = function(opts) { var selectedIDs = []; for (var i = 0; i < selectionManager.selections.length; i++) { - selectedIDs.push(selectionManager.selections[i].id); + selectedIDs.push(selectionManager.selections[i].id); // ? } var data = { @@ -70,7 +70,6 @@ EntityListTool = function(opts) { } selectionManager.setSelections(entityIDs); if (data.focus) { - cameraManager.enable(); cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); diff --git a/examples/toys/flashlight/flashlight.js b/examples/toys/flashlight/flashlight.js index e0106dbd49..165f693e8e 100644 --- a/examples/toys/flashlight/flashlight.js +++ b/examples/toys/flashlight/flashlight.js @@ -16,7 +16,7 @@ (function() { function debugPrint(message) { - // print(message); + //print(message); } Script.include("../../libraries/utils.js"); @@ -31,6 +31,15 @@ _this._spotlight = null; }; + + GRAB_FRAME_USER_DATA_KEY = "grabFrame"; + + // These constants define the Flashlight model Grab Frame + var MODEL_GRAB_FRAME = { + relativePosition: {x: 0, y: -0.1, z: 0}, + relativeRotation: Quat.angleAxis(180, {x: 1, y: 0, z: 0}) + }; + // These constants define the Spotlight position and orientation relative to the model var MODEL_LIGHT_POSITION = {x: 0, y: 0, z: 0}; var MODEL_LIGHT_ROTATION = Quat.angleAxis (-90, {x: 1, y: 0, z: 0}); @@ -43,6 +52,8 @@ Flashlight.prototype = { + + // update() will be called regulary, because we've hooked the update signal in our preload() function // we will check out userData for the grabData. In the case of the hydraGrab script, it will tell us // if we're currently being grabbed and if the person grabbing us is the current interfaces avatar. @@ -126,11 +137,20 @@ // * connecting to the update signal so we can check our grabbed state preload: function(entityID) { _this.entityID = entityID; - + var modelProperties = Entities.getEntityProperties(entityID); _this._startModelPosition = modelProperties.position; _this._startModelRotation = modelProperties.rotation; + // Make sure the Flashlight entity has a correct grab frame setup + var userData = getEntityUserData(entityID); + debugPrint(JSON.stringify(userData)); + if (!userData.grabFrame) { + setEntityCustomData(GRAB_FRAME_USER_DATA_KEY, entityID, MODEL_GRAB_FRAME); + debugPrint(JSON.stringify(MODEL_GRAB_FRAME)); + debugPrint("Assigned the grab frmae for the Flashlight entity"); + } + Script.update.connect(this.update); }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ec4b2c6b8e..7ad568bcc3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -142,6 +142,7 @@ #include "SpeechRecognizer.h" #endif +#include "ui/AddressBarDialog.h" #include "ui/AvatarInputs.h" #include "ui/DataWebDialog.h" #include "ui/DialogsManager.h" @@ -149,7 +150,6 @@ #include "ui/Snapshot.h" #include "ui/StandAloneJSConsole.h" #include "ui/Stats.h" -#include "ui/AddressBarDialog.h" #include "ui/UpdateDialog.h" #include "ui/overlays/Cube3DOverlay.h" @@ -3033,7 +3033,7 @@ int Application::sendNackPackets() { if (node->getActiveSocket() && node->getType() == NodeType::EntityServer) { - NLPacketList nackPacketList(PacketType::OctreeDataNack); + auto nackPacketList = NLPacketList::create(PacketType::OctreeDataNack); QUuid nodeUUID = node->getUUID(); @@ -3062,15 +3062,15 @@ int Application::sendNackPackets() { auto it = missingSequenceNumbers.constBegin(); while (it != missingSequenceNumbers.constEnd()) { OCTREE_PACKET_SEQUENCE missingNumber = *it; - nackPacketList.writePrimitive(missingNumber); + nackPacketList->writePrimitive(missingNumber); ++it; } - if (nackPacketList.getNumPackets()) { - packetsSent += nackPacketList.getNumPackets(); + if (nackPacketList->getNumPackets()) { + packetsSent += nackPacketList->getNumPackets(); // send the packet list - nodeList->sendPacketList(nackPacketList, *node); + nodeList->sendPacketList(std::move(nackPacketList), *node); } } }); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 27b69554db..cce1e67a13 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1295,7 +1295,8 @@ void MyAvatar::initAnimGraph() { // // or run a local web-server // python -m SimpleHTTPServer& - auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/e58e0a24cc341ad5d060/raw/2a994bef7726ce8e9efcee7622b8b1a1b6b67490/ik-avatar.json"); + // auto graphUrl = QUrl("http://localhost:8000/avatar.json"); + auto graphUrl = QUrl("https://gist.githubusercontent.com/hyperlogic/e58e0a24cc341ad5d060/raw/8f824da2908fd89ad1befadd1d8f5d7b3b6efa66/ik-avatar.json"); _rig->initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8a05b15c4c..41ca193a7a 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -116,6 +116,17 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.leanForward = head->getFinalLeanForward(); params.torsoTwist = head->getTorsoTwist(); params.localHeadOrientation = head->getFinalOrientationInLocalFrame(); + params.localHeadPitch = head->getFinalPitch(); + params.localHeadYaw = head->getFinalYaw(); + params.localHeadRoll = head->getFinalRoll(); + params.isInHMD = qApp->getAvatarUpdater()->isHMDMode(); + + // get HMD position from sensor space into world space, and back into model space + glm::mat4 worldToModel = glm::inverse(createMatFromQuatAndPos(myAvatar->getOrientation(), myAvatar->getPosition())); + glm::vec3 yAxis(0.0f, 1.0f, 0.0f); + glm::vec3 hmdPosition = glm::angleAxis((float)M_PI, yAxis) * transformPoint(worldToModel * myAvatar->getSensorToWorldMatrix(), myAvatar->getHMDSensorPosition()); + params.localHeadPosition = hmdPosition; + params.worldHeadOrientation = head->getFinalOrientationInWorldFrame(); params.eyeLookAt = head->getLookAtPosition(); params.eyeSaccade = head->getSaccade(); diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index 5d27af444a..b208c15264 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -1,5 +1,6 @@ // // AddressBarDialog.cpp +// interface/src/ui // // Created by Bradley Austin Davis on 2015/04/14 // Copyright 2015 High Fidelity, Inc. diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index eec3acdbfc..811e2f5b12 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -1,5 +1,6 @@ // // AddressBarDialog.h +// interface/src/ui // // Created by Bradley Austin Davis on 2015/04/14 // Copyright 2015 High Fidelity, Inc. diff --git a/interface/src/ui/AssetUploadDialogFactory.cpp b/interface/src/ui/AssetUploadDialogFactory.cpp index 7d87c17b16..2ffbb298d0 100644 --- a/interface/src/ui/AssetUploadDialogFactory.cpp +++ b/interface/src/ui/AssetUploadDialogFactory.cpp @@ -69,7 +69,7 @@ void AssetUploadDialogFactory::showDialog() { } void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const QString& hash) { - if (upload->getResult() == AssetUpload::Success) { + if (upload->getError() == AssetUpload::NoError) { // show message box for successful upload, with copiable text for ATP hash QDialog* hashCopyDialog = new QDialog(_dialogParent); @@ -124,14 +124,14 @@ void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const Q // figure out the right error message for the message box QString additionalError; - switch (upload->getResult()) { + switch (upload->getError()) { case AssetUpload::PermissionDenied: additionalError = PERMISSION_DENIED_ERROR; break; case AssetUpload::TooLarge: additionalError = "The uploaded content was too large and could not be stored in the asset-server."; break; - case AssetUpload::ErrorLoadingFile: + case AssetUpload::FileOpenError: additionalError = "The file could not be opened. Please check your permissions and try again."; break; default: diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 68d371021a..d54e91b9d6 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -21,7 +21,6 @@ class QAction; -class AddressBarDialog; class AnimationsDialog; class AttachmentsDialog; class AudioStatsDialog; @@ -29,13 +28,11 @@ class BandwidthDialog; class CachesSizeDialog; class DiskCacheEditor; class LodToolsDialog; -class LoginDialog; class OctreeStatsDialog; class PreferencesDialog; class ScriptEditorWindow; class QMessageBox; class DomainConnectionDialog; -class UpdateDialog; class DialogsManager : public QObject, public Dependency { Q_OBJECT @@ -94,7 +91,6 @@ private: } } - QPointer _addressBarDialog; QPointer _animationsDialog; QPointer _attachmentsDialog; QPointer _audioStatsDialog; @@ -104,12 +100,10 @@ private: QPointer _ircInfoBox; QPointer _hmdToolsDialog; QPointer _lodToolsDialog; - QPointer _loginDialog; QPointer _octreeStatsDialog; QPointer _preferencesDialog; QPointer _scriptEditor; QPointer _domainConnectionDialog; - QPointer _updateDialog; }; #endif // hifi_DialogsManager_h diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index b6d2be11f2..786c91c6c1 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -40,7 +40,7 @@ void AnimInverseKinematics::loadPoses(const AnimPoseVec& poses) { void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) const { int numJoints = (int)_relativePoses.size(); absolutePoses.clear(); - absolutePoses.reserve(numJoints); + absolutePoses.resize(numJoints); assert(numJoints <= _skeleton->getNumJoints()); for (int i = 0; i < numJoints; ++i) { int parentIndex = _skeleton->getParentIndex(i); diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 1d720ba565..ac84aafc32 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -149,6 +149,7 @@ public: void set(const std::string& key, const glm::quat& value) { _map[key] = AnimVariant(value); } void set(const std::string& key, const glm::mat4& value) { _map[key] = AnimVariant(value); } void set(const std::string& key, const std::string& value) { _map[key] = AnimVariant(value); } + void unset(const std::string& key) { _map.erase(key); } void setTrigger(const std::string& key) { _triggers.insert(key); } void clearTriggers() { _triggers.clear(); } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index e0a2b31622..3729dc8ca7 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -30,6 +30,9 @@ void Rig::HeadParameters::dump() const { qCDebug(animation, " localHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta); axis = glm::axis(worldHeadOrientation); theta = glm::angle(worldHeadOrientation); + qCDebug(animation, " localHead pitch = %.5f, yaw = %.5f, roll = %.5f", (double)localHeadPitch, (double)localHeadYaw, (double)localHeadRoll); + qCDebug(animation, " localHeadPosition = (%.5f, %.5f, %.5f)", (double)localHeadPosition.x, (double)localHeadPosition.y, (double)localHeadPosition.z); + qCDebug(animation, " isInHMD = %s", isInHMD ? "true" : "false"); qCDebug(animation, " worldHeadOrientation axis = (%.5f, %.5f, %.5f), theta = %0.5f", (double)axis.x, (double)axis.y, (double)axis.z, (double)theta); axis = glm::axis(modelRotation); theta = glm::angle(modelRotation); @@ -953,56 +956,70 @@ void Rig::updateFromHeadParameters(const HeadParameters& params) { if (params.enableLean) { updateLeanJoint(params.leanJointIndex, params.leanSideways, params.leanForward, params.torsoTwist); } - updateNeckJoint(params.neckJointIndex, params.localHeadOrientation, params.leanSideways, params.leanForward, params.torsoTwist); + updateNeckJoint(params.neckJointIndex, params); updateEyeJoints(params.leftEyeJointIndex, params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, params.worldHeadOrientation, params.eyeLookAt, params.eyeSaccade); } +static const glm::vec3 X_AXIS(1.0f, 0.0f, 0.0f); +static const glm::vec3 Y_AXIS(0.0f, 1.0f, 0.0f); +static const glm::vec3 Z_AXIS(0.0f, 0.0f, 1.0f); + void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) { if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { if (_enableAnimGraph && _animSkeleton) { - glm::vec3 xAxis(1.0f, 0.0f, 0.0f); - glm::vec3 yAxis(0.0f, 1.0f, 0.0f); - glm::vec3 zAxis(0.0f, 0.0f, 1.0f); - glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, zAxis) * - glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, xAxis) * - glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, yAxis)); + glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, Z_AXIS) * + glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, X_AXIS) * + glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, Y_AXIS)); _animVars.set("lean", absRot); } else if (!_enableAnimGraph) { auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; // get the rotation axes in joint space and use them to adjust the rotation - glm::vec3 xAxis(1.0f, 0.0f, 0.0f); - glm::vec3 yAxis(0.0f, 1.0f, 0.0f); - glm::vec3 zAxis(0.0f, 0.0f, 1.0f); glm::quat inverse = glm::inverse(parentState.getRotation() * getJointDefaultRotationInParentFrame(index)); setJointRotationInConstrainedFrame(index, - glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * zAxis) * - glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * xAxis) * - glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * yAxis) * + glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * Z_AXIS) * + glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * X_AXIS) * + glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * Y_AXIS) * getJointState(index).getDefaultRotation(), DEFAULT_PRIORITY); } } } -void Rig::updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist) { +void Rig::updateNeckJoint(int index, const HeadParameters& params) { if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { - auto& state = _jointStates[index]; - auto& parentState = _jointStates[state.getParentIndex()]; + if (_enableAnimGraph && _animSkeleton) { + // the params.localHeadOrientation is composed incorrectly, so re-compose it correctly from pitch, yaw and roll. + glm::quat realLocalHeadOrientation = (glm::angleAxis(glm::radians(-params.localHeadRoll), Z_AXIS) * + glm::angleAxis(glm::radians(params.localHeadYaw), Y_AXIS) * + glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS)); + _animVars.set("headRotation", realLocalHeadOrientation); - // get the rotation axes in joint space and use them to adjust the rotation - glm::mat3 axes = glm::mat3_cast(glm::quat()); - glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * - glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) * - state.getPreTransform() * glm::mat4_cast(state.getPreRotation()))); - glm::vec3 pitchYawRoll = safeEulerAngles(localHeadOrientation); - glm::vec3 lean = glm::radians(glm::vec3(leanForward, torsoTwist, leanSideways)); - pitchYawRoll -= lean; - setJointRotationInConstrainedFrame(index, - glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) * - glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) * - glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) * - state.getDefaultRotation(), DEFAULT_PRIORITY); + auto rootTrans = _animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; + + if (params.isInHMD) { + _animVars.set("headPosition", params.localHeadPosition + rootTrans); + } else { + _animVars.unset("headPosition"); + } + } else if (!_enableAnimGraph) { + + auto& state = _jointStates[index]; + auto& parentState = _jointStates[state.getParentIndex()]; + + // get the rotation axes in joint space and use them to adjust the rotation + glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * + glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) * + state.getPreTransform() * glm::mat4_cast(state.getPreRotation()))); + glm::vec3 pitchYawRoll = safeEulerAngles(params.localHeadOrientation); + glm::vec3 lean = glm::radians(glm::vec3(params.leanForward, params.torsoTwist, params.leanSideways)); + pitchYawRoll -= lean; + setJointRotationInConstrainedFrame(index, + glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * Z_AXIS)) * + glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * Y_AXIS)) * + glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * X_AXIS)) * + state.getDefaultRotation(), DEFAULT_PRIORITY); + } } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 861f095b9c..18e888d2b0 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -59,6 +59,11 @@ public: bool enableLean = false; glm::quat modelRotation = glm::quat(); glm::quat localHeadOrientation = glm::quat(); + float localHeadPitch = 0.0f; // degrees + float localHeadYaw = 0.0f; // degrees + float localHeadRoll = 0.0f; // degrees + glm::vec3 localHeadPosition = glm::vec3(); + bool isInHMD = false; glm::quat worldHeadOrientation = glm::quat(); glm::vec3 eyeLookAt = glm::vec3(); // world space glm::vec3 eyeSaccade = glm::vec3(); // world space @@ -178,7 +183,7 @@ public: protected: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); - void updateNeckJoint(int index, const glm::quat& localHeadOrientation, float leanSideways, float leanForward, float torsoTwist); + void updateNeckJoint(int index, const HeadParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); QVector _jointStates; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index fa8c0eb633..a24461da83 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -113,10 +113,10 @@ void EntityTreeRenderer::init() { if (_wantScripts) { _entitiesScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities", - _scriptingServices->getControllerScriptingInterface()); + _scriptingServices->getControllerScriptingInterface(), false); _scriptingServices->registerScriptEngineWithApplicationServices(_entitiesScriptEngine); - _sandboxScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities Sandbox", NULL); + _sandboxScriptEngine = new ScriptEngine(NO_SCRIPT, "Entities Sandbox", NULL, false); } // make sure our "last avatar position" is something other than our current position, so that on our @@ -242,6 +242,7 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool QString scriptContents = loadScriptContents(entityScript, isURL, isPending, url, reload); if (isPending && isPreload && isURL) { + //qDebug() << "attempted to load script, isPending, _waitingOnPreload.insert() url:" << url << "entityID:" << entityID; _waitingOnPreload.insert(url, entityID); } @@ -324,7 +325,8 @@ void EntityTreeRenderer::update() { QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, _lastMouseEvent); QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); if (currentClickingEntity.property("holdingClickOnEntity").isValid()) { - currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs); + //qDebug() << "About to call holdingClickOnEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("holdingClickOnEntity", currentClickingEntity, currentClickingEntityArgs); } } @@ -363,7 +365,9 @@ void EntityTreeRenderer::checkEnterLeaveEntities() { QScriptValueList entityArgs = createEntityArgs(entityID); QScriptValue entityScript = loadEntityScript(entityID); if (entityScript.property("leaveEntity").isValid()) { - entityScript.property("leaveEntity").call(entityScript, entityArgs); + + //qDebug() << "About to call leaveEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("leaveEntity", entityScript, entityArgs); } } @@ -376,7 +380,8 @@ void EntityTreeRenderer::checkEnterLeaveEntities() { QScriptValueList entityArgs = createEntityArgs(entityID); QScriptValue entityScript = loadEntityScript(entityID); if (entityScript.property("enterEntity").isValid()) { - entityScript.property("enterEntity").call(entityScript, entityArgs); + //qDebug() << "About to call enterEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("enterEntity", entityScript, entityArgs); } } } @@ -395,7 +400,8 @@ void EntityTreeRenderer::leaveAllEntities() { QScriptValueList entityArgs = createEntityArgs(entityID); QScriptValue entityScript = loadEntityScript(entityID); if (entityScript.property("leaveEntity").isValid()) { - entityScript.property("leaveEntity").call(entityScript, entityArgs); + //qDebug() << "About to call leaveEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("leaveEntity", entityScript, entityArgs); } } _currentEntitiesInside.clear(); @@ -851,7 +857,7 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device bool precisionPicking = !_dontDoPrecisionPicking; RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking); if (rayPickResult.intersects) { - //qCDebug(entitiesrenderer) << "mousePressEvent over entity:" << rayPickResult.entityID; + qCDebug(entitiesrenderer) << "mousePressEvent over entity:" << rayPickResult.entityID; QString urlString = rayPickResult.properties.getHref(); QUrl url = QUrl(urlString, QUrl::StrictMode); @@ -865,13 +871,15 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); QScriptValue entityScript = loadEntityScript(rayPickResult.entity); if (entityScript.property("mousePressOnEntity").isValid()) { - entityScript.property("mousePressOnEntity").call(entityScript, entityScriptArgs); + //qDebug() << "About to call mousePressOnEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("mousePressOnEntity", entityScript, entityScriptArgs); } _currentClickingOnEntityID = rayPickResult.entityID; emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID)); if (entityScript.property("clickDownOnEntity").isValid()) { - entityScript.property("clickDownOnEntity").call(entityScript, entityScriptArgs); + //qDebug() << "About to call clickDownOnEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("clickDownOnEntity", entityScript, entityScriptArgs); } } else { emit mousePressOffEntity(rayPickResult, event, deviceID); @@ -897,7 +905,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi QScriptValueList entityScriptArgs = createMouseEventArgs(rayPickResult.entityID, event, deviceID); QScriptValue entityScript = loadEntityScript(rayPickResult.entity); if (entityScript.property("mouseReleaseOnEntity").isValid()) { - entityScript.property("mouseReleaseOnEntity").call(entityScript, entityScriptArgs); + _entitiesScriptEngine->callScriptMethod("mouseReleaseOnEntity", entityScript, entityScriptArgs); } } @@ -909,7 +917,8 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi QScriptValueList currentClickingEntityArgs = createMouseEventArgs(_currentClickingOnEntityID, event, deviceID); QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); if (currentClickingEntity.property("clickReleaseOnEntity").isValid()) { - currentClickingEntity.property("clickReleaseOnEntity").call(currentClickingEntity, currentClickingEntityArgs); + //qDebug() << "About to call clickReleaseOnEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("clickReleaseOnEntity", currentClickingEntity, currentClickingEntityArgs); } } @@ -937,11 +946,13 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI // load the entity script if needed... QScriptValue entityScript = loadEntityScript(rayPickResult.entity); if (entityScript.property("mouseMoveEvent").isValid()) { - entityScript.property("mouseMoveEvent").call(entityScript, entityScriptArgs); + //qDebug() << "About to call mouseMoveEvent() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("mouseMoveEvent", entityScript, entityScriptArgs); } emit mouseMoveOnEntity(rayPickResult, event, deviceID); if (entityScript.property("mouseMoveOnEntity").isValid()) { - entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs); + //qDebug() << "About to call mouseMoveOnEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("mouseMoveOnEntity", entityScript, entityScriptArgs); } // handle the hover logic... @@ -955,7 +966,9 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID); if (currentHoverEntity.property("hoverLeaveEntity").isValid()) { - currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs); + //qDebug() << "About to call hoverLeaveEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("hoverLeaveEntity", currentHoverEntity, currentHoverEntityArgs); + } } @@ -964,7 +977,8 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI if (rayPickResult.entityID != _currentHoverOverEntityID) { emit hoverEnterEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); if (entityScript.property("hoverEnterEntity").isValid()) { - entityScript.property("hoverEnterEntity").call(entityScript, entityScriptArgs); + //qDebug() << "About to call hoverEnterEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("hoverEnterEntity", entityScript, entityScriptArgs); } } @@ -972,7 +986,8 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI // we should send our hover over event emit hoverOverEntity(rayPickResult.entityID, MouseEvent(*event, deviceID)); if (entityScript.property("hoverOverEntity").isValid()) { - entityScript.property("hoverOverEntity").call(entityScript, entityScriptArgs); + //qDebug() << "About to call hoverOverEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("hoverOverEntity", entityScript, entityScriptArgs); } // remember what we're hovering over @@ -989,7 +1004,8 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI QScriptValue currentHoverEntity = loadEntityScript(_currentHoverOverEntityID); if (currentHoverEntity.property("hoverLeaveEntity").isValid()) { - currentHoverEntity.property("hoverLeaveEntity").call(currentHoverEntity, currentHoverEntityArgs); + //qDebug() << "About to call hoverLeaveEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("hoverLeaveEntity", currentHoverEntity, currentHoverEntityArgs); } _currentHoverOverEntityID = UNKNOWN_ENTITY_ID; // makes it the unknown ID @@ -1005,7 +1021,8 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI QScriptValue currentClickingEntity = loadEntityScript(_currentClickingOnEntityID); if (currentClickingEntity.property("holdingClickOnEntity").isValid()) { - currentClickingEntity.property("holdingClickOnEntity").call(currentClickingEntity, currentClickingEntityArgs); + //qDebug() << "About to call holdingClickOnEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("holdingClickOnEntity", currentClickingEntity, currentClickingEntityArgs); } } _lastMouseEvent = MouseEvent(*event, deviceID); @@ -1060,7 +1077,8 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const QScriptValue entityScript = loadEntityScript(entityID, true, reload); // is preload! if (entityScript.property("preload").isValid()) { QScriptValueList entityArgs = createEntityArgs(entityID); - entityScript.property("preload").call(entityScript, entityArgs); + //qDebug() << "About to call preload() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("preload", entityScript, entityArgs); } } } @@ -1070,7 +1088,8 @@ void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) { QScriptValue entityScript = getPreviouslyLoadedEntityScript(entityID); if (entityScript.property("unload").isValid()) { QScriptValueList entityArgs = createEntityArgs(entityID); - entityScript.property("unload").call(entityScript, entityArgs); + //qDebug() << "About to call unload() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("unload", entityScript, entityArgs); } } } @@ -1155,7 +1174,9 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons args << idA.toScriptValue(_entitiesScriptEngine); args << idB.toScriptValue(_entitiesScriptEngine); args << collisionToScriptValue(_entitiesScriptEngine, collision); - entityScriptA.property("collisionWithEntity").call(entityScriptA, args); + + //qDebug() << "About to call collisionWithEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("collisionWithEntity", entityScriptA, args); } emit collisionWithEntity(idB, idA, collision); @@ -1165,7 +1186,9 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons args << idB.toScriptValue(_entitiesScriptEngine); args << idA.toScriptValue(_entitiesScriptEngine); args << collisionToScriptValue(_entitiesScriptEngine, collision); - entityScriptB.property("collisionWithEntity").call(entityScriptA, args); + + //qDebug() << "About to call collisionWithEntity() current thread:" << QThread::currentThread() << "entities thread:" << _entitiesScriptEngine->thread(); + _entitiesScriptEngine->callScriptMethod("collisionWithEntity", entityScriptB, args); } } diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 9931bab5ed..2de5173b76 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -205,7 +205,7 @@ bool AssetClient::uploadAsset(const QByteArray& data, const QString& extension, SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); if (assetServer) { - auto packetList = std::unique_ptr(new NLPacketList(PacketType::AssetUpload, QByteArray(), true, true)); + auto packetList = NLPacketList::create(PacketType::AssetUpload, QByteArray(), true, true); auto messageID = ++_currentID; packetList->writePrimitive(messageID); diff --git a/libraries/networking/src/AssetClient.h b/libraries/networking/src/AssetClient.h index 7fa1771873..d2f0bcddf6 100644 --- a/libraries/networking/src/AssetClient.h +++ b/libraries/networking/src/AssetClient.h @@ -29,9 +29,9 @@ struct AssetInfo { int64_t size; }; -using ReceivedAssetCallback = std::function; -using GetInfoCallback = std::function; -using UploadResultCallback = std::function; +using ReceivedAssetCallback = std::function; +using GetInfoCallback = std::function; +using UploadResultCallback = std::function; class AssetClient : public QObject, public Dependency { Q_OBJECT diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index f989f10ae8..b2442cb33d 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -41,14 +41,17 @@ void AssetRequest::start() { _state = WAITING_FOR_INFO; auto assetClient = DependencyManager::get(); - assetClient->getAssetInfo(_hash, _extension, [this](AssetServerError error, AssetInfo info) { + assetClient->getAssetInfo(_hash, _extension, [this](AssetServerError serverError, AssetInfo info) { _info = info; - _error = error; - if (_error != NoError) { + if (serverError != AssetServerError::NoError) { qCDebug(networking) << "Got error retrieving asset info for" << _hash; + _state = FINISHED; emit finished(this); + + _error = (serverError == AssetServerError::AssetNotFound) ? NotFound : UnknownError; + return; } @@ -60,17 +63,38 @@ void AssetRequest::start() { int start = 0, end = _info.size; auto assetClient = DependencyManager::get(); - assetClient->getAsset(_hash, _extension, start, end, [this, start, end](AssetServerError error, + assetClient->getAsset(_hash, _extension, start, end, [this, start, end](AssetServerError serverError, const QByteArray& data) { Q_ASSERT(data.size() == (end - start)); - _error = error; - if (_error == NoError) { - memcpy(_data.data() + start, data.constData(), data.size()); - _totalReceived += data.size(); - emit progress(_totalReceived, _info.size); + if (serverError == AssetServerError::NoError) { + + // we need to check the hash of the received data to make sure it matches what we expect + if (hashData(data).toHex() == _hash) { + memcpy(_data.data() + start, data.constData(), data.size()); + _totalReceived += data.size(); + emit progress(_totalReceived, _info.size); + } else { + // hash doesn't match - we have an error + _error = HashVerificationFailed; + } + } else { - qCDebug(networking) << "Got error retrieving asset" << _hash; + switch (serverError) { + case AssetServerError::AssetNotFound: + _error = NotFound; + break; + case AssetServerError::InvalidByteRange: + _error = InvalidByteRange; + break; + default: + _error = UnknownError; + break; + } + } + + if (_error != NoError) { + qCDebug(networking) << "Got error retrieving asset" << _hash << "- error code" << _error; } _state = FINISHED; diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index b33954d34e..ba3863ffdd 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -29,14 +29,22 @@ public: WAITING_FOR_DATA, FINISHED }; + + enum Error { + NoError, + NotFound, + InvalidByteRange, + HashVerificationFailed, + UnknownError + }; AssetRequest(QObject* parent, const QString& hash, const QString& extension); Q_INVOKABLE void start(); const QByteArray& getData() const { return _data; } - State getState() const { return _state; } - AssetServerError getError() const { return _error; } + const State& getState() const { return _state; } + const Error& getError() const { return _error; } signals: void finished(AssetRequest* thisRequest); @@ -44,7 +52,7 @@ signals: private: State _state = NOT_STARTED; - AssetServerError _error; + Error _error = NoError; AssetInfo _info; uint64_t _totalReceived { 0 }; QString _hash; diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index b9adba99b7..df5cb6464b 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -48,11 +48,11 @@ void AssetResourceRequest::doSend() { Q_ASSERT(req->getState() == AssetRequest::FINISHED); switch (req->getError()) { - case NoError: + case AssetRequest::Error::NoError: _data = req->getData(); _result = Success; break; - case AssetNotFound: + case AssetRequest::Error::NotFound: _result = NotFound; break; default: diff --git a/libraries/networking/src/AssetUpload.cpp b/libraries/networking/src/AssetUpload.cpp index 03c8707517..f51cb4a7b3 100644 --- a/libraries/networking/src/AssetUpload.cpp +++ b/libraries/networking/src/AssetUpload.cpp @@ -46,23 +46,23 @@ void AssetUpload::start() { assetClient->uploadAsset(data, _extension, [this](AssetServerError error, const QString& hash){ switch (error) { case AssetServerError::NoError: - _result = Success; + _error = NoError; break; case AssetServerError::AssetTooLarge: - _result = TooLarge; + _error = TooLarge; break; case AssetServerError::PermissionDenied: - _result = PermissionDenied; + _error = PermissionDenied; break; default: - _result = ErrorLoadingFile; + _error = FileOpenError; break; } emit finished(this, hash); }); } else { // we couldn't open the file - set the error result - _result = ErrorLoadingFile; + _error = FileOpenError; // emit that we are done emit finished(this, QString()); diff --git a/libraries/networking/src/AssetUpload.h b/libraries/networking/src/AssetUpload.h index 0fef814a07..2dfada06f2 100644 --- a/libraries/networking/src/AssetUpload.h +++ b/libraries/networking/src/AssetUpload.h @@ -26,12 +26,12 @@ class AssetUpload : public QObject { Q_OBJECT public: - enum Result { - Success = 0, + enum Error { + NoError = 0, Timeout, TooLarge, PermissionDenied, - ErrorLoadingFile + FileOpenError }; AssetUpload(QObject* parent, const QString& filename); @@ -40,7 +40,7 @@ public: const QString& getFilename() const { return _filename; } const QString& getExtension() const { return _extension; } - const Result& getResult() const { return _result; } + const Error& getError() const { return _error; } signals: void finished(AssetUpload* upload, const QString& hash); @@ -49,7 +49,7 @@ signals: private: QString _filename; QString _extension; - Result _result; + Error _error; }; #endif // hifi_AssetUpload_h diff --git a/libraries/networking/src/JSONBreakableMarshal.cpp b/libraries/networking/src/JSONBreakableMarshal.cpp deleted file mode 100644 index d71d173947..0000000000 --- a/libraries/networking/src/JSONBreakableMarshal.cpp +++ /dev/null @@ -1,338 +0,0 @@ -// -// JSONBreakableMarshal.cpp -// libraries/networking/src -// -// Created by Stephen Birarda on 04/28/15. -// Copyright 2015 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 "JSONBreakableMarshal.h" - -#include -#include -#include - -QVariantMap JSONBreakableMarshal::_interpolationMap = QVariantMap(); - -QStringList JSONBreakableMarshal::toStringList(const QJsonValue& jsonValue, const QString& keypath) { - // setup the string list that will hold our result - QStringList result; - - // figure out what type of value this is so we know how to act on it - if (jsonValue.isObject()) { - - QJsonObject jsonObject = jsonValue.toObject(); - - // enumerate the keys of the QJsonObject - foreach(const QString& key, jsonObject.keys()) { - QJsonValue childValue = jsonObject[key]; - - // setup the keypath for this key - QString valueKeypath = (keypath.isEmpty() ? "" : keypath + ".") + key; - - if (childValue.isObject() || childValue.isArray()) { - // recursion is required since the value is a QJsonObject or QJsonArray - result << toStringList(childValue, valueKeypath); - } else { - // no recursion required, call our toString method to get the string representation - // append the QStringList resulting from that to our QStringList - result << toString(childValue, valueKeypath); - } - } - } else if (jsonValue.isArray()) { - QJsonArray jsonArray = jsonValue.toArray(); - - // enumerate the elements in this QJsonArray - for (int i = 0; i < jsonArray.size(); i++) { - QJsonValue arrayValue = jsonArray[i]; - - // setup the keypath for this object with the array index - QString valueKeypath = QString("%1[%2]").arg(keypath).arg(i); - - if (arrayValue.isObject() || arrayValue.isArray()) { - // recursion is required since the value is a QJsonObject or QJsonArray - // append the QStringList resulting from that to our QStringList - result << toStringList(arrayValue, valueKeypath); - } else { - result << toString(arrayValue, valueKeypath); - } - } - } else { - // this is a basic value, so set result to whatever toString reports in a QStringList - result = QStringList() << toString(jsonValue, keypath); - } - - return result; -} - -const QString JSON_NULL_AS_STRING = "null"; -const QString JSON_TRUE_AS_STRING = "true"; -const QString JSON_FALSE_AS_STRING = "false"; -const QString JSON_UNDEFINED_AS_STRING = "undefined"; -const QString JSON_UNKNOWN_AS_STRING = "unknown"; - -QString JSONBreakableMarshal::toString(const QJsonValue& jsonValue, const QString& keypath) { - // default the value as a string to unknown in case conversion fails - QString valueAsString = JSON_UNKNOWN_AS_STRING; - - // ask the QJsonValue what type it is and format its value as a string accordingly - if (jsonValue.isNull()) { - valueAsString = JSON_NULL_AS_STRING; - } else if (jsonValue.isBool()) { - valueAsString = jsonValue.toBool() ? JSON_TRUE_AS_STRING : JSON_FALSE_AS_STRING; - } else if (jsonValue.isDouble()) { - valueAsString = QString::number(jsonValue.toDouble()); - } else if (jsonValue.isString()) { - valueAsString = QString("\"%1\"").arg(jsonValue.toString()); - } else if (jsonValue.isUndefined()) { - valueAsString = JSON_UNDEFINED_AS_STRING; - } else if (jsonValue.isArray() || jsonValue.isObject()) { - qDebug() << "JSONBreakableMarshal::toString does not handle conversion of a QJsonObject or QJsonArray." - << "You should call JSONBreakableMarshal::toStringList instead."; - } else { - qDebug() << "Unrecognized QJsonValue - JSONBreakableMarshal cannot convert to string."; - } - - return QString("%1=%2").arg(keypath, valueAsString); -} - -QVariant JSONBreakableMarshal::fromString(const QString& marshalValue) { - // default the value to null - QVariant result; - - // attempt to match the value with our expected strings - if (marshalValue == JSON_NULL_AS_STRING) { - // this is already our default, we don't need to do anything here - } else if (marshalValue == JSON_TRUE_AS_STRING || marshalValue == JSON_FALSE_AS_STRING) { - result = QVariant(marshalValue == JSON_TRUE_AS_STRING ? true : false); - } else if (marshalValue == JSON_UNDEFINED_AS_STRING) { - result = JSON_UNDEFINED_AS_STRING; - } else if (marshalValue == JSON_UNKNOWN_AS_STRING) { - // we weren't able to marshal this value at the other end, set it as our unknown string - result = JSON_UNKNOWN_AS_STRING; - } else { - // this might be a double, see if it converts - bool didConvert = false; - double convertResult = marshalValue.toDouble(&didConvert); - - if (didConvert) { - result = convertResult; - } else { - // we need to figure out if this is a string - // use a regex to look for surrounding quotes first - const QString JSON_STRING_REGEX = "^\"([\\s\\S]*)\"$"; - QRegExp stringRegex(JSON_STRING_REGEX); - - if (stringRegex.indexIn(marshalValue) != -1) { - // set the result to the string value - result = stringRegex.cap(1); - } else { - // we failed to convert the value to anything, set the result to our unknown value - qDebug() << "Unrecognized output from JSONBreakableMarshal - could not convert" - << marshalValue << "to QVariant."; - result = JSON_UNKNOWN_AS_STRING; - } - } - } - - return result; -} - -QVariantMap JSONBreakableMarshal::fromStringList(const QStringList& stringList) { - QVariant result = QVariantMap(); - - foreach(const QString& marshalString, stringList) { - - // find the equality operator - int equalityIndex = marshalString.indexOf('='); - - // bail on parsing if we didn't find the equality operator - if (equalityIndex != -1) { - - QVariant* currentValue = &result; - - // pull the key (everything left of the equality sign) - QString parentKeypath; - QString keypath = marshalString.left(equalityIndex); - - // setup for array index checking - const QString ARRAY_INDEX_REGEX_STRING = "\\[(\\d+)\\]"; - QRegExp arrayRegex(ARRAY_INDEX_REGEX_STRING); - - // as long as we have a keypath we need to recurse downwards - while (!keypath.isEmpty()) { - parentKeypath = keypath; - - int arrayBracketIndex = arrayRegex.indexIn(keypath); - - // is this an array index - if (arrayBracketIndex == 0) { - // we're here because we think the current value should be an array - // if it isn't then make one - if (!currentValue->canConvert(QMetaType::QVariantList) || currentValue->isNull()) { - *currentValue = QVariantList(); - } - - QVariantList& currentList = *static_cast(currentValue->data()); - - // figure out what index we want to get the QJsonValue& for - bool didConvert = false; - int arrayIndex = arrayRegex.cap(1).toInt(&didConvert); - - if (didConvert) { - // check if we need to resize the array - if (currentList.size() < arrayIndex + 1) { - - for (int i = currentList.size(); i < arrayIndex + 1; i++) { - // add the null QJsonValue at this array index to get the array to the right size - currentList.push_back(QJsonValue()); - } - } - - // set our currentValue to the QJsonValue& from the array at this index - currentValue = ¤tList[arrayIndex]; - - // update the keypath by bumping past the array index - keypath = keypath.mid(keypath.indexOf(']') + 1); - - // check if there is a key after the array index - if so push the keypath forward by a char - if (keypath.startsWith(".")) { - keypath = keypath.mid(1); - } - } else { - qDebug() << "Failed to convert array index from keypath" << keypath << "to int. Will not add" - << "value to resulting QJsonObject."; - break; - } - - } else { - int keySeparatorIndex = keypath.indexOf('.'); - - // we need to figure out what the key to look at is - QString subKey = keypath; - - if (keySeparatorIndex != -1 || arrayBracketIndex != -1) { - int nextBreakIndex = -1; - int nextKeypathStartIndex = -1; - - if (arrayBracketIndex == -1 || (keySeparatorIndex != -1 && keySeparatorIndex < arrayBracketIndex)) { - nextBreakIndex = keySeparatorIndex; - nextKeypathStartIndex = keySeparatorIndex + 1; - } else if (keySeparatorIndex == -1 || (arrayBracketIndex != -1 - && arrayBracketIndex < keySeparatorIndex)) { - nextBreakIndex = arrayBracketIndex; - nextKeypathStartIndex = arrayBracketIndex; - } else { - qDebug() << "Unrecognized key format while trying to parse " << keypath << " - will not add" - << "value to resulting QJsonObject."; - break; - } - - // set the current key from the determined index - subKey = keypath.left(nextBreakIndex); - - // update the keypath being processed - keypath = keypath.mid(nextKeypathStartIndex); - - } else { - // update the keypath being processed, since we have no more separators in the keypath, it should - // be an empty string - keypath = ""; - } - - // we're here becuase we know the current value should be an object - // if it isn't then make it one - - if (!currentValue->canConvert(QMetaType::QVariantMap) || currentValue->isNull()) { - *currentValue = QVariantMap(); - } - - QVariantMap& currentMap = *static_cast(currentValue->data()); - - // is there a QJsonObject for this key yet? - // if not then we make it now - if (!currentMap.contains(subKey)) { - currentMap[subKey] = QVariant(); - } - - // change the currentValue to the QJsonValue for this key - currentValue = ¤tMap[subKey]; - } - } - - *currentValue = fromString(marshalString.mid(equalityIndex + 1)); - - if (_interpolationMap.contains(parentKeypath)) { - // we expect the currentValue here to be a string, that's the key we use for interpolation - // bail if it isn't - if (currentValue->canConvert(QMetaType::QString) - && _interpolationMap[parentKeypath].canConvert(QMetaType::QVariantMap)) { - *currentValue = _interpolationMap[parentKeypath].toMap()[currentValue->toString()]; - } - } - } - } - - return result.toMap(); -} - -QVariantMap JSONBreakableMarshal::fromStringBuffer(const QByteArray& buffer) { - // this is a packet of strings sep by null terminators - pull out each string and create a stringlist - QStringList packetList; - int currentIndex = 0; - int currentSeparator = buffer.indexOf('\0'); - - while (currentIndex < buffer.size() - 1) { - packetList << QString::fromUtf8(buffer.mid(currentIndex, currentSeparator)); - - if (currentSeparator == -1) { - // no more separators to be found, break out of here so we're not looping for nothing - break; - } - - // bump the currentIndex up to the last found separator - currentIndex = currentSeparator + 1; - - // find the index of the next separator, assuming this one wasn't the last one in the packet - if (currentSeparator < buffer.size() - 1) { - currentSeparator = buffer.indexOf('\0', currentIndex); - } - } - - // now that we have a QStringList we use our static method to turn that into a QJsonObject - return fromStringList(packetList); -} - -void JSONBreakableMarshal::addInterpolationForKey(const QString& rootKey, const QString& interpolationKey, - const QJsonValue& interpolationValue) { - // if there is no map already beneath this key in our _interpolationMap create a QVariantMap there now - - if (!_interpolationMap.contains(rootKey)) { - _interpolationMap.insert(rootKey, QVariantMap()); - } - - if (_interpolationMap[rootKey].canConvert(QMetaType::QVariantMap)) { - QVariantMap& mapForRootKey = *static_cast(_interpolationMap[rootKey].data()); - - mapForRootKey.insert(interpolationKey, QVariant(interpolationValue)); - } else { - qDebug() << "JSONBreakableMarshal::addInterpolationForKey could not convert variant at key" << rootKey - << "to a QVariantMap. Can not add interpolation."; - } -} - -void JSONBreakableMarshal::removeInterpolationForKey(const QString& rootKey, const QString& interpolationKey) { - // make sure the interpolation map contains this root key and that the value is a map - - if (_interpolationMap.contains(rootKey)) { - QVariant& rootValue = _interpolationMap[rootKey]; - - if (!rootValue.isNull() && rootValue.canConvert(QMetaType::QVariantMap)) { - // remove the value at the interpolationKey - static_cast(rootValue.data())->remove(interpolationKey); - } - } -} diff --git a/libraries/networking/src/JSONBreakableMarshal.h b/libraries/networking/src/JSONBreakableMarshal.h deleted file mode 100644 index 287fed675a..0000000000 --- a/libraries/networking/src/JSONBreakableMarshal.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// JSONBreakableMarshal.h -// libraries/networking/src -// -// Created by Stephen Birarda on 04/28/15. -// Copyright 2015 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 -// - -#ifndef hifi_JSONBreakableMarshal_h -#define hifi_JSONBreakableMarshal_h - -#pragma once - -#include -#include -#include -#include - -class JSONBreakableMarshal { -public: - static QStringList toStringList(const QJsonValue& jsonValue, const QString& keypath); - static QString toString(const QJsonValue& jsonValue, const QString& keyPath); - - static QVariant fromString(const QString& marshalValue); - static QVariantMap fromStringList(const QStringList& stringList); - static QVariantMap fromStringBuffer(const QByteArray& buffer); - - static void addInterpolationForKey(const QString& rootKey, - const QString& interpolationKey, const QJsonValue& interpolationValue); - static void removeInterpolationForKey(const QString& rootKey, const QString& interpolationKey); - -private: - static QVariantMap _interpolationMap; -}; - -#endif // hifi_JSONBreakableMarshal_h diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index b6c3db3562..c13a82f821 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -355,6 +355,12 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList, // close the last packet in the list packetList->closeCurrentPacket(); + for (std::unique_ptr& packet : packetList->_packets) { + NLPacket* nlPacket = static_cast(packet.get()); + collectPacketStats(*nlPacket); + fillPacketHeader(*nlPacket); + } + return _nodeSocket.writePacketList(std::move(packetList), sockAddr); } @@ -410,11 +416,12 @@ void LimitedNodeList::eraseAllNodes() { killedNodes.insert(node); }); - // iterate the current nodes, emit that they are dying and remove them from the hash - _nodeMutex.lockForWrite(); - _nodeHash.clear(); - _nodeMutex.unlock(); - + { + // iterate the current nodes, emit that they are dying and remove them from the hash + QWriteLocker writeLocker(&_nodeMutex); + _nodeHash.clear(); + } + foreach(const SharedNodePointer& killedNode, killedNodes) { handleNodeKill(killedNode); } @@ -428,21 +435,20 @@ void LimitedNodeList::reset() { } void LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { - _nodeMutex.lockForRead(); + QReadLocker readLocker(&_nodeMutex); NodeHash::iterator it = _nodeHash.find(nodeUUID); if (it != _nodeHash.end()) { SharedNodePointer matchingNode = it->second; - _nodeMutex.unlock(); - - _nodeMutex.lockForWrite(); - _nodeHash.unsafe_erase(it); - _nodeMutex.unlock(); - + readLocker.unlock(); + + { + QWriteLocker writeLocker(&_nodeMutex); + _nodeHash.unsafe_erase(it); + } + handleNodeKill(matchingNode); - } else { - _nodeMutex.unlock(); } } @@ -839,6 +845,14 @@ void LimitedNodeList::sendPeerQueryToIceServer(const HifiSockAddr& iceServerSock sendPacketToIceServer(PacketType::ICEServerQuery, iceServerSockAddr, clientID, peerID); } +SharedNodePointer LimitedNodeList::findNodeWithAddr(const HifiSockAddr& addr) { + QReadLocker locker(&_nodeMutex); + auto it = std::find_if(std::begin(_nodeHash), std::end(_nodeHash), [&](const UUIDNodePair& pair) { + return pair.second->getActiveSocket() ? (*pair.second->getActiveSocket() == addr) : false; + }); + return (it != std::end(_nodeHash)) ? it->second : SharedNodePointer(); +} + void LimitedNodeList::sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID) { auto icePacket = NLPacket::create(packetType); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index f1d4887b70..719387ace1 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -165,6 +165,8 @@ public: void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr); void sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID); + SharedNodePointer findNodeWithAddr(const HifiSockAddr& addr); + template void eachNode(NodeLambda functor) { QReadLocker readLock(&_nodeMutex); @@ -216,6 +218,7 @@ public: { QReadLocker readLock(&_connectionTimeLock); return _lastConnectionTimes; } void flagTimeForConnectionStep(ConnectionStep connectionStep); + udt::Socket::StatsVector sampleStatsForAllConnections() { return _nodeSocket.sampleStatsForAllConnections(); } public slots: void reset(); @@ -254,7 +257,7 @@ protected: qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr, const QUuid& connectionSecret = QUuid()); void collectPacketStats(const NLPacket& packet); - void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret); + void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret = QUuid()); bool isPacketVerified(const udt::Packet& packet); bool packetVersionMatch(const udt::Packet& packet); diff --git a/libraries/networking/src/NLPacketList.cpp b/libraries/networking/src/NLPacketList.cpp index 6dfd627271..3b115c558b 100644 --- a/libraries/networking/src/NLPacketList.cpp +++ b/libraries/networking/src/NLPacketList.cpp @@ -13,16 +13,30 @@ #include "udt/Packet.h" + +std::unique_ptr NLPacketList::create(PacketType packetType, QByteArray extendedHeader, + bool isReliable, bool isOrdered) { + auto nlPacketList = std::unique_ptr(new NLPacketList(packetType, extendedHeader, + isReliable, isOrdered)); + nlPacketList->open(WriteOnly); + return nlPacketList; +} + +std::unique_ptr NLPacketList::fromPacketList(std::unique_ptr packetList) { + auto nlPacketList = std::unique_ptr(new NLPacketList(std::move(*packetList.release()))); nlPacketList->open(ReadOnly); + return nlPacketList; +} + + NLPacketList::NLPacketList(PacketType packetType, QByteArray extendedHeader, bool isReliable, bool isOrdered) : PacketList(packetType, extendedHeader, isReliable, isOrdered) { } -NLPacketList::NLPacketList(PacketList&& other) : PacketList(other.getType(), other.getExtendedHeader(), other.isReliable(), other.isOrdered()) { +NLPacketList::NLPacketList(PacketList&& other) : PacketList(std::move(other)) { // Update _packets - for (auto& packet : other._packets) { - auto nlPacket = NLPacket::fromBase(std::move(packet)); - _packets.push_back(std::move(nlPacket)); + for (auto& packet : _packets) { + packet = NLPacket::fromBase(std::move(packet)); } if (_packets.size() > 0) { diff --git a/libraries/networking/src/NLPacketList.h b/libraries/networking/src/NLPacketList.h index 5391e49488..c705e6ece3 100644 --- a/libraries/networking/src/NLPacketList.h +++ b/libraries/networking/src/NLPacketList.h @@ -18,12 +18,17 @@ class NLPacketList : public udt::PacketList { public: - NLPacketList(PacketType packetType, QByteArray extendedHeader = QByteArray(), bool isReliable = false, bool isOrdered = false); - NLPacketList(PacketList&& packetList); + static std::unique_ptr create(PacketType packetType, QByteArray extendedHeader = QByteArray(), + bool isReliable = false, bool isOrdered = false); + + static std::unique_ptr fromPacketList(std::unique_ptr); const QUuid& getSourceID() const { return _sourceID; } private: + NLPacketList(PacketType packetType, QByteArray extendedHeader = QByteArray(), bool isReliable = false, + bool isOrdered = false); + NLPacketList(PacketList&& packetList); NLPacketList(const NLPacketList& other) = delete; NLPacketList& operator=(const NLPacketList& other) = delete; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index cf202fa83a..b262904c63 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -27,7 +27,6 @@ #include "AddressManager.h" #include "Assignment.h" #include "HifiSockAddr.h" -#include "JSONBreakableMarshal.h" #include "NetworkLogging.h" #include "udt/PacketHeaders.h" @@ -107,20 +106,12 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned } qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& destination) { - NLPacketList statsPacketList(PacketType::NodeJsonStats); + auto statsPacketList = NLPacketList::create(PacketType::NodeJsonStats, QByteArray(), true, true); - // get a QStringList using JSONBreakableMarshal - QStringList statsStringList = JSONBreakableMarshal::toStringList(statsObject, ""); + QJsonDocument jsonDocument(statsObject); + statsPacketList->write(jsonDocument.toBinaryData()); - // enumerate the resulting strings - pack them and send off packets via NLPacketList - foreach(const QString& statsItem, statsStringList) { - QByteArray utf8String = statsItem.toUtf8(); - utf8String.append('\0'); - - statsPacketList.write(utf8String); - } - - sendPacketList(statsPacketList, destination); + sendPacketList(std::move(statsPacketList), destination); // enumerate the resulting strings, breaking them into MTU sized packets return 0; diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index fb1e2d896f..0efb8bba7c 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -247,7 +247,7 @@ void PacketReceiver::handleVerifiedPacketList(std::unique_ptr p } // setup an NLPacketList from the PacketList we were passed - auto nlPacketList = new NLPacketList(std::move(*packetList)); + auto nlPacketList = NLPacketList::fromPacketList(std::move(packetList)); auto nodeList = DependencyManager::get(); @@ -297,21 +297,21 @@ void PacketReceiver::handleVerifiedPacketList(std::unique_ptr p success = metaMethod.invoke(listener.first, connectionType, Q_ARG(QSharedPointer, - QSharedPointer(nlPacketList)), + QSharedPointer(nlPacketList.release())), Q_ARG(SharedNodePointer, matchingNode)); } else if (metaMethod.parameterTypes().contains(QSHAREDPOINTER_NODE_NORMALIZED)) { success = metaMethod.invoke(listener.first, connectionType, Q_ARG(QSharedPointer, - QSharedPointer(nlPacketList)), + QSharedPointer(nlPacketList.release())), Q_ARG(QSharedPointer, matchingNode)); } else { success = metaMethod.invoke(listener.first, connectionType, Q_ARG(QSharedPointer, - QSharedPointer(nlPacketList))); + QSharedPointer(nlPacketList.release()))); } } else { listenerIsDead = true; @@ -323,7 +323,7 @@ void PacketReceiver::handleVerifiedPacketList(std::unique_ptr p if (listener.first) { success = listener.second.invoke(listener.first, Q_ARG(QSharedPointer, - QSharedPointer(nlPacketList))); + QSharedPointer(nlPacketList.release()))); } else { listenerIsDead = true; } diff --git a/libraries/networking/src/udt/BasePacket.h b/libraries/networking/src/udt/BasePacket.h index 4e13d99013..cc18c75a80 100644 --- a/libraries/networking/src/udt/BasePacket.h +++ b/libraries/networking/src/udt/BasePacket.h @@ -31,8 +31,6 @@ public: static std::unique_ptr create(qint64 size = -1); static std::unique_ptr fromReceivedPacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); - - // Current level's header size static int localHeaderSize(); @@ -72,8 +70,8 @@ public: virtual bool isSequential() const { return false; } virtual bool reset(); virtual qint64 size() const { return _payloadCapacity; } - - using QIODevice::read; + + using QIODevice::read; // Bring QIODevice::read methods to scope, otherwise they are hidden by folling method QByteArray read(qint64 maxSize); QByteArray readWithoutCopy(qint64 maxSize); // this can only be used if packet will stay in scope @@ -107,16 +105,16 @@ protected: }; template qint64 BasePacket::peekPrimitive(T* data) { - return QIODevice::peek(reinterpret_cast(data), sizeof(T)); + return peek(reinterpret_cast(data), sizeof(T)); } template qint64 BasePacket::readPrimitive(T* data) { - return QIODevice::read(reinterpret_cast(data), sizeof(T)); + return read(reinterpret_cast(data), sizeof(T)); } template qint64 BasePacket::writePrimitive(const T& data) { static_assert(!std::is_pointer::value, "T must not be a pointer"); - return QIODevice::write(reinterpret_cast(&data), sizeof(T)); + return write(reinterpret_cast(&data), sizeof(T)); } } // namespace udt diff --git a/libraries/networking/src/udt/ConnectionStats.cpp b/libraries/networking/src/udt/ConnectionStats.cpp index 52188c29c3..4cc9b17e64 100644 --- a/libraries/networking/src/udt/ConnectionStats.cpp +++ b/libraries/networking/src/udt/ConnectionStats.cpp @@ -15,7 +15,7 @@ using namespace udt; using namespace std::chrono; ConnectionStats::ConnectionStats() { - auto now = duration_cast(high_resolution_clock::now().time_since_epoch()); + auto now = duration_cast(system_clock::now().time_since_epoch()); _currentSample.startTime = now; _total.startTime = now; } @@ -24,7 +24,7 @@ ConnectionStats::Stats ConnectionStats::sample() { Stats sample = _currentSample; _currentSample = Stats(); - auto now = duration_cast(high_resolution_clock::now().time_since_epoch()); + auto now = duration_cast(system_clock::now().time_since_epoch()); sample.endTime = now; _currentSample.startTime = now; diff --git a/libraries/networking/src/udt/ConnectionStats.h b/libraries/networking/src/udt/ConnectionStats.h index 494d433bca..e04a836ca8 100644 --- a/libraries/networking/src/udt/ConnectionStats.h +++ b/libraries/networking/src/udt/ConnectionStats.h @@ -36,11 +36,13 @@ public: SentTimeoutNAK, ReceivedTimeoutNAK, Retransmission, - Duplicate + Duplicate, + + NumEvents }; // construct a vector for the events of the size of our Enum - default value is zero - std::vector events = std::vector((int) Event::Duplicate + 1, 0); + std::vector events = std::vector((int) Event::NumEvents, 0); // packet counts and sizes int sentPackets { 0 }; diff --git a/libraries/networking/src/udt/PacketList.cpp b/libraries/networking/src/udt/PacketList.cpp index 7b90276b62..53edfb32f1 100644 --- a/libraries/networking/src/udt/PacketList.cpp +++ b/libraries/networking/src/udt/PacketList.cpp @@ -15,6 +15,21 @@ using namespace udt; +std::unique_ptr PacketList::create(PacketType packetType, QByteArray extendedHeader, + bool isReliable, bool isOrdered) { + auto packetList = std::unique_ptr(new PacketList(packetType, extendedHeader, + isReliable, isOrdered)); + packetList->open(WriteOnly); + return packetList; +} + +std::unique_ptr PacketList::fromReceivedPackets(std::list>&& packets) { + auto packetList = std::unique_ptr(new PacketList(PacketType::Unknown, QByteArray(), true, true)); + packetList->_packets = std::move(packets); + packetList->open(ReadOnly); + return packetList; +} + PacketList::PacketList(PacketType packetType, QByteArray extendedHeader, bool isReliable, bool isOrdered) : _packetType(packetType), _isReliable(isReliable), @@ -22,14 +37,15 @@ PacketList::PacketList(PacketType packetType, QByteArray extendedHeader, bool is _extendedHeader(extendedHeader) { Q_ASSERT_X(!(!_isReliable && _isOrdered), "PacketList", "Unreliable ordered PacketLists are not currently supported"); - QIODevice::open(WriteOnly); } PacketList::PacketList(PacketList&& other) : + _packetType(other._packetType), _packets(std::move(other._packets)), - _packetType(other._packetType) + _isReliable(other._isReliable), + _isOrdered(other._isOrdered), + _extendedHeader(std::move(other._extendedHeader)) { - } void PacketList::startSegment() { @@ -66,12 +82,6 @@ size_t PacketList::getMessageSize() const { return totalBytes; } -std::unique_ptr PacketList::fromReceivedPackets(std::list>&& packets) { - auto packetList = std::unique_ptr(new PacketList(PacketType::Unknown, QByteArray(), true, true)); - packetList->_packets = std::move(packets); - return packetList; -} - std::unique_ptr PacketList::createPacket() { // use the static create method to create a new packet // If this packet list is supposed to be ordered then we consider this to be part of a message @@ -94,6 +104,17 @@ std::unique_ptr PacketList::createPacketWithExtendedHeader() { return packet; } +void PacketList::closeCurrentPacket(bool shouldSendEmpty) { + if (shouldSendEmpty && !_currentPacket) { + _currentPacket = createPacketWithExtendedHeader(); + } + + if (_currentPacket) { + // move the current packet to our list of packets + _packets.push_back(std::move(_currentPacket)); + } +} + QByteArray PacketList::getMessage() { size_t sizeBytes = 0; @@ -191,14 +212,3 @@ qint64 PacketList::writeData(const char* data, qint64 maxSize) { return maxSize; } - -void PacketList::closeCurrentPacket(bool shouldSendEmpty) { - if (shouldSendEmpty && !_currentPacket) { - _currentPacket = createPacketWithExtendedHeader(); - } - - if (_currentPacket) { - // move the current packet to our list of packets - _packets.push_back(std::move(_currentPacket)); - } -} diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index 05800a1b26..ae783dabe3 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -28,13 +28,9 @@ class Packet; class PacketList : public QIODevice { Q_OBJECT public: - PacketList(PacketType packetType, QByteArray extendedHeader = QByteArray(), bool isReliable = false, bool isOrdered = false); - PacketList(PacketList&& other); - + static std::unique_ptr create(PacketType packetType, QByteArray extendedHeader = QByteArray(), bool isReliable = false, bool isOrdered = false); static std::unique_ptr fromReceivedPackets(std::list>&& packets); - virtual bool isSequential() const { return true; } - bool isReliable() const { return _isReliable; } bool isOrdered() const { return _isOrdered; } @@ -53,20 +49,28 @@ public: QByteArray getMessage(); + // QIODevice virtual functions + virtual bool isSequential() const { return false; } + virtual qint64 size() const { return getDataSize(); } + template qint64 readPrimitive(T* data); template qint64 writePrimitive(const T& data); - std::list> _packets; + protected: - virtual qint64 writeData(const char* data, qint64 maxSize); - virtual qint64 readData(char* data, qint64 maxSize) { return 0; } - PacketType _packetType; + PacketList(PacketType packetType, QByteArray extendedHeader = QByteArray(), bool isReliable = false, bool isOrdered = false); + PacketList(PacketList&& other); + virtual qint64 writeData(const char* data, qint64 maxSize); + // Not implemented, added an assert so that it doesn't get used by accident + virtual qint64 readData(char* data, qint64 maxSize) { Q_ASSERT(false); return 0; } + + PacketType _packetType; + std::list> _packets; private: friend class ::LimitedNodeList; - friend class Socket; friend class SendQueue; - friend class NLPacketList; + friend class Socket; PacketList(const PacketList& other) = delete; PacketList& operator=(const PacketList& other) = delete; @@ -91,12 +95,12 @@ private: template qint64 PacketList::readPrimitive(T* data) { static_assert(!std::is_pointer::value, "T must not be a pointer"); - return QIODevice::read(reinterpret_cast(data), sizeof(T)); + return read(reinterpret_cast(data), sizeof(T)); } template qint64 PacketList::writePrimitive(const T& data) { static_assert(!std::is_pointer::value, "T must not be a pointer"); - return QIODevice::write(reinterpret_cast(&data), sizeof(T)); + return write(reinterpret_cast(&data), sizeof(T)); } template std::unique_ptr PacketList::takeFront() { diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 37bc88ac49..a0aa767de1 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -325,6 +325,16 @@ ConnectionStats::Stats Socket::sampleStatsForConnection(const HifiSockAddr& dest } } +Socket::StatsVector Socket::sampleStatsForAllConnections() { + StatsVector result; + result.reserve(_connectionsHash.size()); + for (const auto& connectionPair : _connectionsHash) { + result.emplace_back(connectionPair.first, connectionPair.second->sampleStats()); + } + return result; +} + + std::vector Socket::getConnectionSockAddrs() { std::vector addr; addr.reserve(_connectionsHash.size()); diff --git a/libraries/networking/src/udt/Socket.h b/libraries/networking/src/udt/Socket.h index 923248caad..457d3feffc 100644 --- a/libraries/networking/src/udt/Socket.h +++ b/libraries/networking/src/udt/Socket.h @@ -46,6 +46,8 @@ using PacketListHandler = std::function)>; class Socket : public QObject { Q_OBJECT public: + using StatsVector = std::vector>; + Socket(QObject* object = 0); quint16 localPort() const { return _udpSocket.localPort(); } @@ -71,6 +73,8 @@ public: void setCongestionControlFactory(std::unique_ptr ccFactory); void messageReceived(std::unique_ptr packetList); + + StatsVector sampleStatsForAllConnections(); public slots: void cleanupConnection(HifiSockAddr sockAddr); @@ -86,6 +90,7 @@ private: // privatized methods used by UDTTest - they are private since they must be called on the Socket thread ConnectionStats::Stats sampleStatsForConnection(const HifiSockAddr& destination); + std::vector getConnectionSockAddrs(); void connectToSendSignal(const HifiSockAddr& destinationAddr, QObject* receiver, const char* slot); diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index b91db94f74..d057bf067f 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -176,6 +176,14 @@ void AnimDebugDraw::removePoses(const std::string& key) { _poses.erase(key); } +void AnimDebugDraw::addPose(const std::string& key, const AnimPose& pose, const AnimPose& rootPose) { + _singlePoses[key] = SinglePoseInfo(pose, rootPose); +} + +void AnimDebugDraw::removePose(const std::string& key) { + _singlePoses.erase(key); +} + static const uint32_t red = toRGBA(255, 0, 0, 255); static const uint32_t green = toRGBA(0, 255, 0, 255); static const uint32_t blue = toRGBA(0, 0, 255, 255); @@ -333,6 +341,7 @@ void AnimDebugDraw::update() { const size_t VERTICES_PER_LINK = 8 * 2; const float BONE_RADIUS = 0.01f; // 1 cm + const float POSE_RADIUS = 0.1f; // 10 cm // figure out how many verts we will need. int numVerts = 0; @@ -371,6 +380,8 @@ void AnimDebugDraw::update() { } } + numVerts += _singlePoses.size() * VERTICES_PER_BONE; + data._vertexBuffer->resize(sizeof(Vertex) * numVerts); Vertex* verts = (Vertex*)data._vertexBuffer->editData(); Vertex* v = verts; @@ -475,6 +486,13 @@ void AnimDebugDraw::update() { } } + for (auto& iter : _singlePoses) { + AnimPose pose = std::get<0>(iter.second); + AnimPose rootPose = std::get<1>(iter.second); + const float radius = POSE_RADIUS / (pose.scale.x * rootPose.scale.x); + addBone(rootPose, pose, radius, v); + } + assert(numVerts == (v - verts)); data._indexBuffer->resize(sizeof(uint16_t) * numVerts); diff --git a/libraries/render-utils/src/AnimDebugDraw.h b/libraries/render-utils/src/AnimDebugDraw.h index cd17f62590..5bb19097f2 100644 --- a/libraries/render-utils/src/AnimDebugDraw.h +++ b/libraries/render-utils/src/AnimDebugDraw.h @@ -27,15 +27,22 @@ public: AnimDebugDraw(); ~AnimDebugDraw(); + // draw a skeleton bind pose void addSkeleton(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color); void removeSkeleton(const std::string& key); + // draw the interal poses for a specific animNode void addAnimNode(const std::string& key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color); void removeAnimNode(const std::string& key); + // draw a set of poses with a skeleton void addPoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color); void removePoses(const std::string& key); + // draw a single pose + void addPose(const std::string& key, const AnimPose& rootPose, const AnimPose& pose); + void removePose(const std::string& key); + void update(); protected: @@ -48,10 +55,12 @@ protected: typedef std::tuple SkeletonInfo; typedef std::tuple AnimNodeInfo; typedef std::tuple PosesInfo; + typedef std::tuple SinglePoseInfo; std::unordered_map _skeletons; std::unordered_map _animNodes; std::unordered_map _poses; + std::unordered_map _singlePoses; // no copies AnimDebugDraw(const AnimDebugDraw&) = delete; diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 9e04cd4ec3..fb523b157c 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -68,6 +68,7 @@ void ScriptCache::scriptDownloaded() { qCDebug(scriptengine) << "Done downloading script at:" << url.toString(); foreach(ScriptUser* user, scriptUsers) { + // FIXME - I sometimes get a crash deep below here inside of Qt while evaluating the script user->scriptContentsAvailable(url, _scriptCache[url]); } } else { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 83a87b718f..9f061a5cdb 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -81,7 +81,7 @@ void inputControllerFromScriptValue(const QScriptValue &object, AbstractInputCon } ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNameString, - AbstractControllerScriptingInterface* controllerScriptingInterface) : + AbstractControllerScriptingInterface* controllerScriptingInterface, bool wantSignals) : _scriptContents(scriptContents), _isFinished(false), @@ -95,6 +95,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _avatarSound(NULL), _numAvatarSoundSentBytes(0), _controllerScriptingInterface(controllerScriptingInterface), + _wantSignals(wantSignals), _avatarData(NULL), _scriptName(), _fileNameString(fileNameString), @@ -260,6 +261,20 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents, const QStrin return true; } +void ScriptEngine::callScriptMethod(QString methodName, QScriptValue script, QScriptValueList args) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, + "callScriptMethod", + Q_ARG(QString, methodName), + Q_ARG(QScriptValue, script), + Q_ARG(QScriptValueList, args)); + return; + } + + //qDebug() << "About to call " << methodName << "() current thread : " << QThread::currentThread() << "engine thread : " << thread(); + script.property(methodName).call(script, args); +} + void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { if (_isRunning) { return; @@ -285,10 +300,14 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { qCDebug(scriptengine) << "ScriptEngine loading file:" << _fileNameString; QTextStream in(&scriptFile); _scriptContents = in.readAll(); - emit scriptLoaded(_fileNameString); + if (_wantSignals) { + emit scriptLoaded(_fileNameString); + } } else { qCDebug(scriptengine) << "ERROR Loading file:" << _fileNameString << "line:" << __LINE__; - emit errorLoadingScript(_fileNameString); + if (_wantSignals) { + emit errorLoadingScript(_fileNameString); + } } } else { bool isPending; @@ -300,12 +319,16 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { void ScriptEngine::scriptContentsAvailable(const QUrl& url, const QString& scriptContents) { _scriptContents = scriptContents; - emit scriptLoaded(_fileNameString); + if (_wantSignals) { + emit scriptLoaded(_fileNameString); + } } void ScriptEngine::errorInLoadingScript(const QUrl& url) { qCDebug(scriptengine) << "ERROR Loading file:" << url.toString() << "line:" << __LINE__; - emit errorLoadingScript(_fileNameString); // ?? + if (_wantSignals) { + emit errorLoadingScript(_fileNameString); // ?? + } } void ScriptEngine::init() { @@ -510,7 +533,9 @@ void ScriptEngine::evaluate() { if (hasUncaughtException()) { int line = uncaughtExceptionLineNumber(); qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString(); - emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + result.toString()); + if (_wantSignals) { + emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + result.toString()); + } clearExceptions(); } } @@ -527,7 +552,9 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString(); } _evaluatesPending--; - emit evaluationFinished(result, hasUncaughtException()); + if (_wantSignals) { + emit evaluationFinished(result, hasUncaughtException()); + } clearExceptions(); return result; } @@ -553,7 +580,9 @@ void ScriptEngine::run() { } _isRunning = true; _isFinished = false; - emit runningStateChanged(); + if (_wantSignals) { + emit runningStateChanged(); + } QScriptValue result = evaluate(_scriptContents); @@ -700,19 +729,25 @@ void ScriptEngine::run() { if (hasUncaughtException()) { int line = uncaughtExceptionLineNumber(); qCDebug(scriptengine) << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << uncaughtException().toString(); - emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + uncaughtException().toString()); + if (_wantSignals) { + emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + uncaughtException().toString()); + } clearExceptions(); } if (!_isFinished) { - emit update(deltaTime); + if (_wantSignals) { + emit update(deltaTime); + } } lastUpdate = now; } stopAllTimers(); // make sure all our timers are stopped if the script is ending - emit scriptEnding(); + if (_wantSignals) { + emit scriptEnding(); + } // kill the avatar identity timer delete _avatarIdentityTimer; @@ -733,12 +768,15 @@ void ScriptEngine::run() { } } - emit finished(_fileNameString); + if (_wantSignals) { + emit finished(_fileNameString); + } _isRunning = false; - emit runningStateChanged(); - - emit doneRunning(); + if (_wantSignals) { + emit runningStateChanged(); + emit doneRunning(); + } _doneRunningThisScript = true; } @@ -757,7 +795,9 @@ void ScriptEngine::stopAllTimers() { void ScriptEngine::stop() { if (!_isFinished) { _isFinished = true; - emit runningStateChanged(); + if (_wantSignals) { + emit runningStateChanged(); + } } } @@ -845,7 +885,9 @@ QUrl ScriptEngine::resolvePath(const QString& include) const { } void ScriptEngine::print(const QString& message) { - emit printedMessage(message); + if (_wantSignals) { + emit printedMessage(message); + } } // If a callback is specified, the included files will be loaded asynchronously and the callback will be called @@ -929,9 +971,13 @@ void ScriptEngine::load(const QString& loadFile) { if (_isReloading) { auto scriptCache = DependencyManager::get(); scriptCache->deleteScript(url.toString()); - emit reloadScript(url.toString(), false); + if (_wantSignals) { + emit reloadScript(url.toString(), false); + } } else { - emit loadScript(url.toString(), false); + if (_wantSignals) { + emit loadScript(url.toString(), false); + } } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index bb2918fb8e..2bfcecc99e 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -45,7 +45,8 @@ class ScriptEngine : public QScriptEngine, public ScriptUser { public: ScriptEngine(const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString(""), - AbstractControllerScriptingInterface* controllerScriptingInterface = NULL); + AbstractControllerScriptingInterface* controllerScriptingInterface = NULL, + bool wantSignals = true); ~ScriptEngine(); @@ -64,6 +65,8 @@ public: int numArguments = -1); Q_INVOKABLE void setIsAvatar(bool isAvatar); + Q_INVOKABLE void callScriptMethod(QString methodName, QScriptValue script, QScriptValueList args); + bool isAvatar() const { return _isAvatar; } void setAvatarData(AvatarData* avatarData, const QString& objectName); @@ -154,6 +157,7 @@ protected: int _numAvatarSoundSentBytes; bool _isAgent = false; QSet _includedURLs; + bool _wantSignals = true; private: void stopAllTimers(); diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index b2389f4db6..d110635f92 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -26,7 +26,8 @@ static int quatMetaTypeId = qRegisterMetaType(); static int xColorMetaTypeId = qRegisterMetaType(); static int pickRayMetaTypeId = qRegisterMetaType(); static int collisionMetaTypeId = qRegisterMetaType(); -static int qMapURLStringMetaTypeId = qRegisterMetaType>(); +static int qMapURLStringMetaTypeId = qRegisterMetaType>(); +static int qScriptValueListMetaTypeId = qRegisterMetaType("QScriptValueList"); void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); diff --git a/tests/animation/src/data/avatar.json b/tests/animation/src/data/avatar.json index 3122fd433d..08425201b0 100644 --- a/tests/animation/src/data/avatar.json +++ b/tests/animation/src/data/avatar.json @@ -22,6 +22,11 @@ "jointName": "LeftHand", "positionVar": "leftHandPosition", "rotationVar": "leftHandRotation" + }, + { + "jointName": "Head", + "positionVar": "headPosition", + "rotationVar": "headRotation" } ] }, diff --git a/tools/udt-test/src/UDTTest.cpp b/tools/udt-test/src/UDTTest.cpp index 9a828ebafb..d57ff7a874 100644 --- a/tools/udt-test/src/UDTTest.cpp +++ b/tools/udt-test/src/UDTTest.cpp @@ -267,9 +267,7 @@ void UDTTest::sendPacket() { if (call++ % refillCount == 0) { // construct a reliable and ordered packet list - auto packetList = std::unique_ptr({ - new udt::PacketList(PacketType::BulkAvatarData, QByteArray(), true, true) - }); + auto packetList = udt::PacketList::create(PacketType::BulkAvatarData, QByteArray(), true, true); // fill the packet list with random data according to the constant seed (so receiver can verify) for (int i = 0; i < messageSizePackets; ++i) {