void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode) { QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); quint8 pingType; quint64 ourOriginalTime, othersReplyTime; packetStream >> pingType >> ourOriginalTime >> othersReplyTime; quint64 now = usecTimestampNow(); int pingTime = now - ourOriginalTime; int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight // The other node's expected time should be our original time plus the one way flight time // anything other than that is clock skew quint64 othersExprectedReply = ourOriginalTime + oneWayFlightTime; int clockSkew = othersReplyTime - othersExprectedReply; sendingNode->setPingMs(pingTime / 1000); sendingNode->updateClockSkewUsec(clockSkew); const bool wantDebug = false; if (wantDebug) { qDebug() << "PING_REPLY from node " << *sendingNode << "\n" << " now: " << now << "\n" << " ourTime: " << ourOriginalTime << "\n" << " pingTime: " << pingTime << "\n" << " oneWayFlightTime: " << oneWayFlightTime << "\n" << " othersReplyTime: " << othersReplyTime << "\n" << " othersExprectedReply: " << othersExprectedReply << "\n" << " clockSkew: " << clockSkew << "\n" << " average clockSkew: " << sendingNode->getClockSkewUsec(); } }
int InjectedAudioRingBuffer::parseData(const QByteArray& packet) { // setup a data stream to read from this packet QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); // push past the stream identifier packetStream.skipRawData(NUM_BYTES_RFC4122_UUID); // pull the loopback flag and set our boolean uchar shouldLoopback; packetStream >> shouldLoopback; _shouldLoopbackForNode = (shouldLoopback == 1); // use parsePositionalData in parent PostionalAudioRingBuffer class to pull common positional data packetStream.skipRawData(parsePositionalData(packet.mid(packetStream.device()->pos()))); // pull out the radius for this injected source - if it's zero this is a point source packetStream >> _radius; quint8 attenuationByte = 0; packetStream >> attenuationByte; _attenuationRatio = attenuationByte / (float) MAX_INJECTOR_VOLUME; packetStream.skipRawData(writeData(packet.data() + packetStream.device()->pos(), packet.size() - packetStream.device()->pos())); return packetStream.device()->pos(); }
ThreadedAssignment* AssignmentFactory::unpackAssignment(const QByteArray& packet) { QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); quint8 packedType; packetStream >> packedType; Assignment::Type unpackedType = (Assignment::Type) packedType; switch (unpackedType) { case Assignment::AudioMixerType: return new AudioMixer(packet); case Assignment::AvatarMixerType: return new AvatarMixer(packet); case Assignment::AgentType: return new Agent(packet); case Assignment::VoxelServerType: return new VoxelServer(packet); case Assignment::ParticleServerType: return new ParticleServer(packet); case Assignment::MetavoxelServerType: return new MetavoxelServer(packet); default: return NULL; } }
int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) { // setup a data stream to read from this packet QDataStream packetStream(packetAfterSeqNum); // skip the stream identifier packetStream.skipRawData(NUM_BYTES_RFC4122_UUID); // pull the loopback flag and set our boolean uchar shouldLoopback; packetStream >> shouldLoopback; _shouldLoopbackForNode = (shouldLoopback == 1); // use parsePositionalData in parent PostionalAudioRingBuffer class to pull common positional data packetStream.skipRawData(parsePositionalData(packetAfterSeqNum.mid(packetStream.device()->pos()))); // pull out the radius for this injected source - if it's zero this is a point source packetStream >> _radius; quint8 attenuationByte = 0; packetStream >> attenuationByte; _attenuationRatio = attenuationByte / (float)MAX_INJECTOR_VOLUME; int numAudioBytes = packetAfterSeqNum.size() - packetStream.device()->pos(); numAudioSamples = numAudioBytes / sizeof(int16_t); return packetStream.device()->pos(); }
void NodeList::processDomainServerAddedNode(QSharedPointer<NLPacket> packet) { // setup a QDataStream QDataStream packetStream(packet.data()); // use our shared method to pull out the new node parseNodeFromPacketStream(packetStream); }
Common::SeekableReadStream *WwRIFFVorbisStream::createPacket() { if (_currentOffset >= (_dataOffset + _dataSize)) return 0; Common::MemoryWriteStreamDynamic header(true); Common::BitStreamWriter8LSB bits(header); Packet audioPacket(*_inStream, _currentOffset, _noGranule); const size_t packerHeaderSize = audioPacket.headerSize(); const size_t size = audioPacket.size(); const size_t packetPayloadOffset = audioPacket.offset(); const size_t nextOffset = audioPacket.nextOffset(); if (_currentOffset + packerHeaderSize > _dataOffset + _dataSize) throw Common::Exception("WwRIFFVorbisStream::createPacket(): " "Page header truncated"); _currentOffset = packetPayloadOffset; _inStream->seek(_currentOffset); Common::ScopedPtr<Common::SeekableReadStream> packetStream(_inStream->readStream(size)); _currentOffset = nextOffset; if (_currentOffset > (_dataOffset + _dataSize)) throw Common::Exception("WwRIFFVorbisStream::createPacket(): " "Page truncated"); return packetStream.release(); }
void NodeList::processDomainServerAddedNode(QSharedPointer<ReceivedMessage> message) { // setup a QDataStream QDataStream packetStream(message->getMessage()); // use our shared method to pull out the new node parseNodeFromPacketStream(packetStream); }
QByteArray LimitedNodeList::constructPingPacket(PingType_t pingType, bool isVerified, const QUuid& packetHeaderID) { QByteArray pingPacket = byteArrayWithPopulatedHeader(isVerified ? PacketTypePing : PacketTypeUnverifiedPing, packetHeaderID); QDataStream packetStream(&pingPacket, QIODevice::Append); packetStream << pingType; packetStream << usecTimestampNow(); return pingPacket; }
void NodeList::sendAssignment(Assignment& assignment) { PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand ? PacketType::CreateAssignment : PacketType::RequestAssignment; auto assignmentPacket = NLPacket::create(assignmentPacketType); QDataStream packetStream(assignmentPacket.get()); packetStream << assignment; sendPacket(std::move(assignmentPacket), _assignmentServerSocket); }
void NodeList::sendAssignment(Assignment& assignment) { PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand ? PacketTypeCreateAssignment : PacketTypeRequestAssignment; QByteArray packet = byteArrayWithPopulatedHeader(assignmentPacketType); QDataStream packetStream(&packet, QIODevice::Append); packetStream << assignment; _nodeSocket.writeDatagram(packet, _assignmentServerSocket.getAddress(), _assignmentServerSocket.getPort()); }
int NodeList::processDomainServerList(const QByteArray& packet) { // this is a packet from the domain server, reset the count of un-replied check-ins _numNoReplyDomainCheckIns = 0; // if this was the first domain-server list from this domain, we've now connected if (!_domainHandler.isConnected()) { _domainHandler.setUUID(uuidFromPacketHeader(packet)); _domainHandler.setIsConnected(true); } int readNodes = 0; // setup variables to read into from QDataStream qint8 nodeType; QUuid nodeUUID, connectionUUID; HifiSockAddr nodePublicSocket; HifiSockAddr nodeLocalSocket; QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); // pull our owner UUID from the packet, it's always the first thing QUuid newUUID; packetStream >> newUUID; setSessionUUID(newUUID); // pull each node in the packet while(packetStream.device()->pos() < packet.size()) { packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket; // if the public socket address is 0 then it's reachable at the same IP // as the domain server if (nodePublicSocket.getAddress().isNull()) { nodePublicSocket.setAddress(_domainHandler.getIP()); } SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, nodeLocalSocket); packetStream >> connectionUUID; node->setConnectionSecret(connectionUUID); } // ping inactive nodes in conjunction with receipt of list from domain-server // this makes it happen every second and also pings any newly added nodes pingInactiveNodes(); return readNodes; }
int PositionalAudioRingBuffer::parsePositionalData(const QByteArray& positionalByteArray) { QDataStream packetStream(positionalByteArray); packetStream.readRawData(reinterpret_cast<char*>(&_position), sizeof(_position)); packetStream.readRawData(reinterpret_cast<char*>(&_orientation), sizeof(_orientation)); // if this node sent us a NaN for first float in orientation then don't consider this good audio and bail if (glm::isnan(_orientation.x)) { reset(); return 0; } return packetStream.device()->pos(); }
void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessage> message) { if (message->getSize() == 0) { return; } QDataStream packetStream(message->getMessage()); // read a NodeConnectionData object from the packet so we can pass around this data while we're inspecting it NodeConnectionData nodeConnection = NodeConnectionData::fromDataStream(packetStream, message->getSenderSockAddr()); QByteArray myProtocolVersion = protocolVersionsSignature(); if (nodeConnection.protocolVersion != myProtocolVersion) { sendProtocolMismatchConnectionDenial(message->getSenderSockAddr()); return; } if (nodeConnection.localSockAddr.isNull() || nodeConnection.publicSockAddr.isNull()) { qDebug() << "Unexpected data received for node local socket or public socket. Will not allow connection."; return; } static const NodeSet VALID_NODE_TYPES { NodeType::AudioMixer, NodeType::AvatarMixer, NodeType::AssetServer, NodeType::EntityServer, NodeType::Agent, NodeType::MessagesMixer, NodeType::EntityScriptServer }; if (!VALID_NODE_TYPES.contains(nodeConnection.nodeType)) { qDebug() << "Received an invalid node type with connect request. Will not allow connection from" << nodeConnection.senderSockAddr << ": " << nodeConnection.nodeType; return; } // check if this connect request matches an assignment in the queue auto pendingAssignment = _pendingAssignedNodes.find(nodeConnection.connectUUID); SharedNodePointer node; if (pendingAssignment != _pendingAssignedNodes.end()) { node = processAssignmentConnectRequest(nodeConnection, pendingAssignment->second); } else if (!STATICALLY_ASSIGNED_NODES.contains(nodeConnection.nodeType)) { QString username; QByteArray usernameSignature; if (message->getBytesLeftToRead() > 0) { // read username from packet packetStream >> username; if (message->getBytesLeftToRead() > 0) { // read user signature from packet packetStream >> usernameSignature; }
void HandshakeResponder::responderParseStarterHello(){ //R:1 parse QByteArray packet; m_socketStream >> packet; QDataStream packetStream(&packet, QIODevice::ReadOnly); if(isError(packet)) return; quint8 secLevel; packetStream >> secLevel; if(static_cast<SecurityLevel>(secLevel) != PreSharedIdentity){ processError(BadSecurityLevel); return; } QByteArray rsaCypherText; packetStream >> rsaCypherText; QByteArray clearText = rsaDecrypt(rsaCypherText); if(clearText.isEmpty()){ processError(DataCorrupted); return; } QDataStream clearTextStream(&clearText, QIODevice::ReadOnly); QByteArray key; clearTextStream >> key; m_contact = m_contactDB->findByKey(key); if(m_contact == NULL){ processError(IdentityCheckFailed); return; } try{ m_rsaEncryptor.AccessKey().Load(ArraySource((byte*)m_contact->getKey().data(), m_contact->getKey().size(), true)); }catch(CryptoPP::BERDecodeErr&){ processError(BadContactKey); } quint8 version; clearTextStream >> version; if((version & 0xF0) != (SUPPORTED_PROTOCOL_VERSION & 0xF0)){ processError(IncompatibleProtocolVersions); return; } updateIntegrityHash(&m_starterIntegrityHash, (char)secLevel+clearText); responderRespondHello(); }
void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode) { // deconstruct this ping packet to see if it is a public or local reply QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); quint8 pingType; packetStream >> pingType; // if this is a local or public ping then we can activate a socket // we do nothing with agnostic pings, those are simply for timing if (pingType == PingType::Local && sendingNode->getActiveSocket() != &sendingNode->getLocalSocket()) { sendingNode->activateLocalSocket(); } else if (pingType == PingType::Public && !sendingNode->getActiveSocket()) { sendingNode->activatePublicSocket(); } else if (pingType == PingType::Symmetric && !sendingNode->getActiveSocket()) { sendingNode->activateSymmetricSocket(); } }
void DataServerClient::putValueForKeyAndUserString(const QString& key, const QString& value, const QString& userString) { // setup the header for this packet and push packetStream to desired spot QByteArray putPacket = byteArrayWithPopulatedHeader(PacketTypeDataServerPut); QDataStream packetStream(&putPacket, QIODevice::Append); // pack our data for the put packet packetStream << _sequenceNumber << userString << key << value; // add the putPacket to our vector of unconfirmed packets, will be deleted once put is confirmed _unmatchedPackets.insert(_sequenceNumber, putPacket); // send this put request to the data server NodeList::getInstance()->getNodeSocket().writeDatagram(putPacket, dataServerSockAddr().getAddress(), dataServerSockAddr().getPort()); // push the sequence number forwards _sequenceNumber++; }
void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message) { if (_domainHandler.getSockAddr().isNull()) { // refuse to process this packet if we aren't currently connected to the DS return; } // this is a packet from the domain server, reset the count of un-replied check-ins _numNoReplyDomainCheckIns = 0; // emit our signal so listeners know we just heard from the DS emit receivedDomainServerList(); DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList); QDataStream packetStream(message->getMessage()); // grab the domain's ID from the beginning of the packet QUuid domainUUID; packetStream >> domainUUID; // if this was the first domain-server list from this domain, we've now connected if (!_domainHandler.isConnected()) { _domainHandler.setUUID(domainUUID); _domainHandler.setIsConnected(true); } // pull our owner UUID from the packet, it's always the first thing QUuid newUUID; packetStream >> newUUID; setSessionUUID(newUUID); quint8 isAllowedEditor; packetStream >> isAllowedEditor; setIsAllowedEditor((bool) isAllowedEditor); quint8 thisNodeCanRez; packetStream >> thisNodeCanRez; setThisNodeCanRez((bool) thisNodeCanRez); // pull each node in the packet while (packetStream.device()->pos() < message->getSize()) { parseNodeFromPacketStream(packetStream); } }
int InjectedAudioStream::parseStreamProperties(PacketType type, const QByteArray& packetAfterSeqNum, int& numAudioSamples) { // setup a data stream to read from this packet QDataStream packetStream(packetAfterSeqNum); // skip the stream identifier packetStream.skipRawData(NUM_BYTES_RFC4122_UUID); // read the channel flag bool isStereo; packetStream >> isStereo; // if isStereo value has changed, restart the ring buffer with new frame size if (isStereo != _isStereo) { _ringBuffer.resizeForFrameSize(isStereo ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); _isStereo = isStereo; } // pull the loopback flag and set our boolean uchar shouldLoopback; packetStream >> shouldLoopback; _shouldLoopbackForNode = (shouldLoopback == 1); // use parsePositionalData in parent PostionalAudioRingBuffer class to pull common positional data packetStream.skipRawData(parsePositionalData(packetAfterSeqNum.mid(packetStream.device()->pos()))); // pull out the radius for this injected source - if it's zero this is a point source packetStream >> _radius; quint8 attenuationByte = 0; packetStream >> attenuationByte; _attenuationRatio = attenuationByte / (float)MAX_INJECTOR_VOLUME; packetStream >> _ignorePenumbra; int numAudioBytes = packetAfterSeqNum.size() - packetStream.device()->pos(); numAudioSamples = numAudioBytes / sizeof(int16_t); return packetStream.device()->pos(); }
void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, const QString& userString, DataServerCallbackObject* callbackObject) { if (!userString.isEmpty() && keys.size() <= UCHAR_MAX) { QByteArray getPacket = byteArrayWithPopulatedHeader(PacketTypeDataServerGet); QDataStream packetStream(&getPacket, QIODevice::Append); // pack our data for the getPacket packetStream << _sequenceNumber << userString << keys.join(MULTI_KEY_VALUE_SEPARATOR); // add the getPacket to our map of unconfirmed packets, will be deleted once we get a response from the nameserver _unmatchedPackets.insert(_sequenceNumber, getPacket); _callbackObjects.insert(_sequenceNumber, callbackObject); // send the get to the data server NodeList::getInstance()->getNodeSocket().writeDatagram(getPacket, dataServerSockAddr().getAddress(), dataServerSockAddr().getPort()); _sequenceNumber++; } }
QByteArray LimitedNodeList::constructPingReplyPacket(const QByteArray& pingPacket, const QUuid& packetHeaderID) { QDataStream pingPacketStream(pingPacket); pingPacketStream.skipRawData(numBytesForPacketHeader(pingPacket)); PingType_t typeFromOriginalPing; pingPacketStream >> typeFromOriginalPing; quint64 timeFromOriginalPing; pingPacketStream >> timeFromOriginalPing; PacketType replyType = (packetTypeForPacket(pingPacket) == PacketTypePing) ? PacketTypePingReply : PacketTypeUnverifiedPingReply; QByteArray replyPacket = byteArrayWithPopulatedHeader(replyType, packetHeaderID); QDataStream packetStream(&replyPacket, QIODevice::Append); packetStream << typeFromOriginalPing << timeFromOriginalPing << usecTimestampNow(); return replyPacket; }
void DataServerClient::processSendFromDataServer(const QByteArray& packet) { // pull the user string from the packet so we know who to associate this with QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); quint8 sequenceNumber = 0; packetStream >> sequenceNumber; if (_callbackObjects.find(sequenceNumber) != _callbackObjects.end()) { // remove the packet from our two maps, it's matched DataServerCallbackObject* callbackObject = _callbackObjects.take(sequenceNumber); _unmatchedPackets.remove(sequenceNumber); QString userString, keyListString, valueListString; packetStream >> userString >> keyListString >> valueListString; callbackObject->processDataServerResponse(userString, keyListString.split(MULTI_KEY_VALUE_SEPARATOR), valueListString.split(MULTI_KEY_VALUE_SEPARATOR)); }
void NodeList::activateSocketFromNodeCommunication(ReceivedMessage& message, const SharedNodePointer& sendingNode) { // deconstruct this ping packet to see if it is a public or local reply QDataStream packetStream(message.getMessage()); quint8 pingType; packetStream >> pingType; // if this is a local or public ping then we can activate a socket // we do nothing with agnostic pings, those are simply for timing if (pingType == PingType::Local && sendingNode->getActiveSocket() != &sendingNode->getLocalSocket()) { sendingNode->activateLocalSocket(); } else if (pingType == PingType::Public && !sendingNode->getActiveSocket()) { sendingNode->activatePublicSocket(); } else if (pingType == PingType::Symmetric && !sendingNode->getActiveSocket()) { sendingNode->activateSymmetricSocket(); } if (sendingNode->getType() == NodeType::AudioMixer) { flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetAudioMixerSocket); } }
void DataServer::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; while (_socket.hasPendingDatagrams()) { receivedPacket.resize(_socket.pendingDatagramSize()); _socket.readDatagram(receivedPacket.data(), _socket.pendingDatagramSize(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); PacketType requestType = packetTypeForPacket(receivedPacket); if ((requestType == PacketTypeDataServerPut || requestType == PacketTypeDataServerGet) && receivedPacket[numBytesArithmeticCodingFromBuffer(receivedPacket.data())] == versionForPacketType(requestType)) { QDataStream packetStream(receivedPacket); int numReceivedHeaderBytes = numBytesForPacketHeader(receivedPacket); packetStream.skipRawData(numReceivedHeaderBytes); // pull the sequence number used for this packet quint8 sequenceNumber = 0; packetStream >> sequenceNumber; // pull the UUID that we will need as part of the key QString userString; packetStream >> userString; QUuid parsedUUID(userString); if (parsedUUID.isNull()) { // we failed to parse a UUID, this means the user has sent us a username // ask redis for the UUID for this user redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(userString)); if (reply->type == REDIS_REPLY_STRING) { parsedUUID = QUuid(QString(reply->str)); } if (!parsedUUID.isNull()) { qDebug() << "Found UUID" << parsedUUID << "for username" << userString; } else { qDebug() << "Failed UUID lookup for username" << userString; } freeReplyObject(reply); reply = NULL; } if (!parsedUUID.isNull()) { if (requestType == PacketTypeDataServerPut) { // pull the key and value that specifies the data the user is putting/getting QString dataKey, dataValue; packetStream >> dataKey >> dataValue; qDebug("Sending command to redis: SET uuid:%s:%s %s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey), qPrintable(dataValue)); redisReply* reply = (redisReply*) redisCommand(_redis, "SET uuid:%s:%s %s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey), qPrintable(dataValue)); if (reply->type == REDIS_REPLY_STATUS && strcmp("OK", reply->str) == 0) { // if redis stored the value successfully reply back with a confirm // which is a reply packet with the sequence number QByteArray replyPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerConfirm, _uuid); replyPacket.append(sequenceNumber); _socket.writeDatagram(replyPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } freeReplyObject(reply); } else { // setup a send packet with the returned data // leverage the packetData sent by overwriting and appending QByteArray sendPacket = byteArrayWithPopluatedHeader(PacketTypeDataServerSend, _uuid); QDataStream sendPacketStream(&sendPacket, QIODevice::Append); sendPacketStream << sequenceNumber; // pull the key list that specifies the data the user is putting/getting QString keyListString; packetStream >> keyListString; if (keyListString != "uuid") { // copy the parsed UUID sendPacketStream << uuidStringWithoutCurlyBraces(parsedUUID); const char MULTI_KEY_VALUE_SEPARATOR = '|'; // append the keyListString back to the sendPacket sendPacketStream << keyListString; QStringList keyList = keyListString.split(MULTI_KEY_VALUE_SEPARATOR); QStringList valueList; foreach (const QString& dataKey, keyList) { qDebug("Sending command to redis: GET uuid:%s:%s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey)); redisReply* reply = (redisReply*) redisCommand(_redis, "GET uuid:%s:%s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey)); if (reply->len) { // copy the value that redis returned valueList << QString(reply->str); } else { // didn't find a value - insert a space valueList << QChar(' '); } freeReplyObject(reply); } // append the value QStringList using the right separator sendPacketStream << valueList.join(MULTI_KEY_VALUE_SEPARATOR); } else {
void ScriptEngine::run() { // TODO: can we add a short circuit for _stoppingAllScripts here? What does it mean to not start running if // we're in the process of stopping? if (!_isInitialized) { init(); } _isRunning = true; _isFinished = false; emit runningStateChanged(); QScriptValue result = evaluate(_scriptContents); QElapsedTimer startTime; startTime.start(); int thisFrame = 0; auto nodeList = DependencyManager::get<NodeList>(); auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>(); qint64 lastUpdate = usecTimestampNow(); while (!_isFinished) { int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec if (usecToSleep > 0) { usleep(usecToSleep); } if (_isFinished) { break; } QCoreApplication::processEvents(); if (_isFinished) { break; } if (!_isFinished && entityScriptingInterface->getEntityPacketSender()->serversExist()) { // release the queue of edit entity messages. entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) { entityScriptingInterface->getEntityPacketSender()->process(); } } if (!_isFinished && _isAvatar && _avatarData) { const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * AudioConstants::SAMPLE_RATE) / (1000 * 1000)) + 0.5); const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t); QByteArray avatarPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarData); avatarPacket.append(_avatarData->toByteArray()); nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer); if (_isListeningToAudioStream || _avatarSound) { // if we have an avatar audio stream then send it out to our audio-mixer bool silentFrame = true; int16_t numAvailableSamples = SCRIPT_AUDIO_BUFFER_SAMPLES; const int16_t* nextSoundOutput = NULL; if (_avatarSound) { const QByteArray& soundByteArray = _avatarSound->getByteArray(); nextSoundOutput = reinterpret_cast<const int16_t*>(soundByteArray.data() + _numAvatarSoundSentBytes); int numAvailableBytes = (soundByteArray.size() - _numAvatarSoundSentBytes) > SCRIPT_AUDIO_BUFFER_BYTES ? SCRIPT_AUDIO_BUFFER_BYTES : soundByteArray.size() - _numAvatarSoundSentBytes; numAvailableSamples = numAvailableBytes / sizeof(int16_t); // check if the all of the _numAvatarAudioBufferSamples to be sent are silence for (int i = 0; i < numAvailableSamples; ++i) { if (nextSoundOutput[i] != 0) { silentFrame = false; break; } } _numAvatarSoundSentBytes += numAvailableBytes; if (_numAvatarSoundSentBytes == soundByteArray.size()) { // we're done with this sound object - so set our pointer back to NULL // and our sent bytes back to zero _avatarSound = NULL; _numAvatarSoundSentBytes = 0; } } QByteArray audioPacket = byteArrayWithPopulatedHeader(silentFrame ? PacketTypeSilentAudioFrame : PacketTypeMicrophoneAudioNoEcho); QDataStream packetStream(&audioPacket, QIODevice::Append); // pack a placeholder value for sequence number for now, will be packed when destination node is known int numPreSequenceNumberBytes = audioPacket.size(); packetStream << (quint16) 0; if (silentFrame) { if (!_isListeningToAudioStream) { // if we have a silent frame and we're not listening then just send nothing and break out of here break; } // write the number of silent samples so the audio-mixer can uphold timing packetStream.writeRawData(reinterpret_cast<const char*>(&SCRIPT_AUDIO_BUFFER_SAMPLES), sizeof(int16_t)); // use the orientation and position of this avatar for the source of this audio packetStream.writeRawData(reinterpret_cast<const char*>(&_avatarData->getPosition()), sizeof(glm::vec3)); glm::quat headOrientation = _avatarData->getHeadOrientation(); packetStream.writeRawData(reinterpret_cast<const char*>(&headOrientation), sizeof(glm::quat)); } else if (nextSoundOutput) { // assume scripted avatar audio is mono and set channel flag to zero packetStream << (quint8)0; // use the orientation and position of this avatar for the source of this audio packetStream.writeRawData(reinterpret_cast<const char*>(&_avatarData->getPosition()), sizeof(glm::vec3)); glm::quat headOrientation = _avatarData->getHeadOrientation(); packetStream.writeRawData(reinterpret_cast<const char*>(&headOrientation), sizeof(glm::quat)); // write the raw audio data packetStream.writeRawData(reinterpret_cast<const char*>(nextSoundOutput), numAvailableSamples * sizeof(int16_t)); } // write audio packet to AudioMixer nodes auto nodeList = DependencyManager::get<NodeList>(); nodeList->eachNode([this, &nodeList, &audioPacket, &numPreSequenceNumberBytes](const SharedNodePointer& node){ // only send to nodes of type AudioMixer if (node->getType() == NodeType::AudioMixer) { // pack sequence number quint16 sequence = _outgoingScriptAudioSequenceNumbers[node->getUUID()]++; memcpy(audioPacket.data() + numPreSequenceNumberBytes, &sequence, sizeof(quint16)); // send audio packet nodeList->writeDatagram(audioPacket, node); } }); } } qint64 now = usecTimestampNow(); float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND; 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()); clearExceptions(); } if (!_isFinished) { emit update(deltaTime); } lastUpdate = now; } stopAllTimers(); // make sure all our timers are stopped if the script is ending emit scriptEnding(); // kill the avatar identity timer delete _avatarIdentityTimer; if (entityScriptingInterface->getEntityPacketSender()->serversExist()) { // release the queue of edit entity messages. entityScriptingInterface->getEntityPacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent if (!entityScriptingInterface->getEntityPacketSender()->isThreaded()) { entityScriptingInterface->getEntityPacketSender()->process(); } } // If we were on a thread, then wait till it's done if (thread()) { thread()->quit(); } emit finished(_fileNameString); _isRunning = false; emit runningStateChanged(); emit doneRunning(); _doneRunningThisScript = true; }
void AudioInjector::injectAudio() { QByteArray soundByteArray = _sound->getByteArray(); // make sure we actually have samples downloaded to inject if (soundByteArray.size()) { // give our sample byte array to the local audio interface, if we have it, so it can be handled locally if (_options.getLoopbackAudioInterface()) { // assume that localAudioInterface could be on a separate thread, use Qt::AutoConnection to handle properly QMetaObject::invokeMethod(_options.getLoopbackAudioInterface(), "handleAudioByteArray", Qt::AutoConnection, Q_ARG(QByteArray, soundByteArray)); } NodeList* nodeList = NodeList::getInstance(); // setup the packet for injected audio QByteArray injectAudioPacket = byteArrayWithPopulatedHeader(PacketTypeInjectAudio); QDataStream packetStream(&injectAudioPacket, QIODevice::Append); packetStream << QUuid::createUuid(); // pack the flag for loopback uchar loopbackFlag = (uchar) (!_options.getLoopbackAudioInterface()); packetStream << loopbackFlag; // pack the position for injected audio packetStream.writeRawData(reinterpret_cast<const char*>(&_options.getPosition()), sizeof(_options.getPosition())); // pack our orientation for injected audio packetStream.writeRawData(reinterpret_cast<const char*>(&_options.getOrientation()), sizeof(_options.getOrientation())); // pack zero for radius float radius = 0; packetStream << radius; // pack 255 for attenuation byte quint8 volume = MAX_INJECTOR_VOLUME * _options.getVolume(); packetStream << volume; QElapsedTimer timer; timer.start(); int nextFrame = 0; int currentSendPosition = 0; int numPreAudioDataBytes = injectAudioPacket.size(); // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks while (currentSendPosition < soundByteArray.size() && !_shouldStop) { int bytesToCopy = std::min(NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL, soundByteArray.size() - currentSendPosition); // resize the QByteArray to the right size injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy); // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet memcpy(injectAudioPacket.data() + numPreAudioDataBytes, soundByteArray.data() + currentSendPosition, bytesToCopy); // grab our audio mixer from the NodeList, if it exists SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); // send off this audio packet nodeList->writeDatagram(injectAudioPacket, audioMixer); currentSendPosition += bytesToCopy; // send two packets before the first sleep so the mixer can start playback right away if (currentSendPosition != bytesToCopy && currentSendPosition < soundByteArray.size()) { // not the first packet and not done // sleep for the appropriate time int usecToSleep = (++nextFrame * BUFFER_SEND_INTERVAL_USECS) - timer.nsecsElapsed() / 1000; if (usecToSleep > 0) { usleep(usecToSleep); } } } } emit finished(); }
void AudioInjector::injectToMixer() { if (_currentSendPosition < 0 || _currentSendPosition >= _audioData.size()) { _currentSendPosition = 0; } auto nodeList = DependencyManager::get<NodeList>(); // make sure we actually have samples downloaded to inject if (_audioData.size()) { // setup the packet for injected audio QByteArray injectAudioPacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeInjectAudio); QDataStream packetStream(&injectAudioPacket, QIODevice::Append); // pack some placeholder sequence number for now int numPreSequenceNumberBytes = injectAudioPacket.size(); packetStream << (quint16)0; // pack stream identifier (a generated UUID) packetStream << QUuid::createUuid(); // pack the stereo/mono type of the stream packetStream << _options.stereo; // pack the flag for loopback uchar loopbackFlag = (uchar) true; packetStream << loopbackFlag; // pack the position for injected audio int positionOptionOffset = injectAudioPacket.size(); packetStream.writeRawData(reinterpret_cast<const char*>(&_options.position), sizeof(_options.position)); // pack our orientation for injected audio int orientationOptionOffset = injectAudioPacket.size(); packetStream.writeRawData(reinterpret_cast<const char*>(&_options.orientation), sizeof(_options.orientation)); // pack zero for radius float radius = 0; packetStream << radius; // pack 255 for attenuation byte int volumeOptionOffset = injectAudioPacket.size(); quint8 volume = MAX_INJECTOR_VOLUME * _options.volume; packetStream << volume; packetStream << _options.ignorePenumbra; QElapsedTimer timer; timer.start(); int nextFrame = 0; int numPreAudioDataBytes = injectAudioPacket.size(); bool shouldLoop = _options.loop; // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks quint16 outgoingInjectedAudioSequenceNumber = 0; while (_currentSendPosition < _audioData.size() && !_shouldStop) { int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, _audioData.size() - _currentSendPosition); // Measure the loudness of this frame _loudness = 0.0f; for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { _loudness += abs(*reinterpret_cast<int16_t*>(_audioData.data() + _currentSendPosition + i)) / (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); } _loudness /= (float)(bytesToCopy / sizeof(int16_t)); memcpy(injectAudioPacket.data() + positionOptionOffset, &_options.position, sizeof(_options.position)); memcpy(injectAudioPacket.data() + orientationOptionOffset, &_options.orientation, sizeof(_options.orientation)); volume = MAX_INJECTOR_VOLUME * _options.volume; memcpy(injectAudioPacket.data() + volumeOptionOffset, &volume, sizeof(volume)); // resize the QByteArray to the right size injectAudioPacket.resize(numPreAudioDataBytes + bytesToCopy); // pack the sequence number memcpy(injectAudioPacket.data() + numPreSequenceNumberBytes, &outgoingInjectedAudioSequenceNumber, sizeof(quint16)); // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet memcpy(injectAudioPacket.data() + numPreAudioDataBytes, _audioData.data() + _currentSendPosition, bytesToCopy); // grab our audio mixer from the NodeList, if it exists SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); // send off this audio packet nodeList->writeDatagram(injectAudioPacket, audioMixer); outgoingInjectedAudioSequenceNumber++; _currentSendPosition += bytesToCopy; // send two packets before the first sleep so the mixer can start playback right away if (_currentSendPosition != bytesToCopy && _currentSendPosition < _audioData.size()) { // process events in case we have been told to stop and be deleted QCoreApplication::processEvents(); if (_shouldStop) { break; } // not the first packet and not done // sleep for the appropriate time int usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000; if (usecToSleep > 0) { usleep(usecToSleep); } } if (shouldLoop && _currentSendPosition >= _audioData.size()) { _currentSendPosition = 0; } } } setIsFinished(true); }
void NodeList::sendDomainServerCheckIn() { if (_isShuttingDown) { qCDebug(networking) << "Refusing to send a domain-server check in while shutting down."; return; } if (_publicSockAddr.isNull()) { // we don't know our public socket and we need to send it to the domain server qCDebug(networking) << "Waiting for inital public socket from STUN. Will not send domain-server check in."; } else if (_domainHandler.getIP().isNull() && _domainHandler.requiresICE()) { qCDebug(networking) << "Waiting for ICE discovered domain-server socket. Will not send domain-server check in."; handleICEConnectionToDomainServer(); } else if (!_domainHandler.getIP().isNull()) { bool isUsingDTLS = false; PacketType domainPacketType = !_domainHandler.isConnected() ? PacketType::DomainConnectRequest : PacketType::DomainListRequest; if (!_domainHandler.isConnected()) { qCDebug(networking) << "Sending connect request to domain-server at" << _domainHandler.getHostname(); // is this our localhost domain-server? // if so we need to make sure we have an up-to-date local port in case it restarted if (_domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost || _domainHandler.getHostname() == "localhost") { quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT; getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, domainPort); qCDebug(networking) << "Local domain-server port read from shared memory (or default) is" << domainPort; _domainHandler.setPort(domainPort); } } // check if we're missing a keypair we need to verify ourselves with the domain-server auto& accountManager = AccountManager::getInstance(); const QUuid& connectionToken = _domainHandler.getConnectionToken(); // we assume that we're on the same box as the DS if it has the same local address and // it didn't present us with a connection token to use for username signature bool localhostDomain = _domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost || (_domainHandler.getSockAddr().getAddress() == _localSockAddr.getAddress() && connectionToken.isNull()); bool requiresUsernameSignature = !_domainHandler.isConnected() && !connectionToken.isNull() && !localhostDomain; if (requiresUsernameSignature && !accountManager.getAccountInfo().hasPrivateKey()) { qWarning() << "A keypair is required to present a username signature to the domain-server" << "but no keypair is present. Waiting for keypair generation to complete."; accountManager.generateNewUserKeypair(); // don't send the check in packet - wait for the keypair first return; } auto domainPacket = NLPacket::create(domainPacketType); QDataStream packetStream(domainPacket.get()); if (domainPacketType == PacketType::DomainConnectRequest) { QUuid connectUUID; if (!_domainHandler.getAssignmentUUID().isNull()) { // this is a connect request and we're an assigned node // so set our packetUUID as the assignment UUID connectUUID = _domainHandler.getAssignmentUUID(); } else if (_domainHandler.requiresICE()) { // this is a connect request and we're an interface client // that used ice to discover the DS // so send our ICE client UUID with the connect request connectUUID = _domainHandler.getICEClientID(); } // pack the connect UUID for this connect request packetStream << connectUUID; } // pack our data to send to the domain-server packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); if (!_domainHandler.isConnected()) { DataServerAccountInfo& accountInfo = accountManager.getAccountInfo(); packetStream << accountInfo.getUsername(); // if this is a connect request, and we can present a username signature, send it along if (requiresUsernameSignature && accountManager.getAccountInfo().hasPrivateKey()) { const QByteArray& usernameSignature = accountManager.getAccountInfo().getUsernameSignature(connectionToken); packetStream << usernameSignature; } } flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn); if (!isUsingDTLS) { sendPacket(std::move(domainPacket), _domainHandler.getSockAddr()); } if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS // so emit our signal that says that emit limitOfSilentDomainCheckInsReached(); } // increment the count of un-replied check-ins _numNoReplyDomainCheckIns++; } }
void NodeList::sendDomainServerCheckIn() { if (_publicSockAddr.isNull() && !_hasCompletedInitialSTUNFailure) { // we don't know our public socket and we need to send it to the domain server // send a STUN request to figure it out sendSTUNRequest(); } else if (_domainHandler.getIP().isNull() && _domainHandler.requiresICE()) { handleICEConnectionToDomainServer(); } else if (!_domainHandler.getIP().isNull()) { bool isUsingDTLS = false; PacketType domainPacketType = !_domainHandler.isConnected() ? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest; if (!_domainHandler.isConnected()) { qDebug() << "Sending connect request to domain-server at" << _domainHandler.getHostname(); } // construct the DS check in packet QUuid packetUUID = _sessionUUID; if (domainPacketType == PacketTypeDomainConnectRequest) { if (!_domainHandler.getAssignmentUUID().isNull()) { // this is a connect request and we're an assigned node // so set our packetUUID as the assignment UUID packetUUID = _domainHandler.getAssignmentUUID(); } else if (_domainHandler.requiresICE()) { // this is a connect request and we're an interface client // that used ice to discover the DS // so send our ICE client UUID with the connect request packetUUID = _domainHandler.getICEClientID(); } } QByteArray domainServerPacket = byteArrayWithPopulatedHeader(domainPacketType, packetUUID); QDataStream packetStream(&domainServerPacket, QIODevice::Append); // pack our data to send to the domain-server packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); // if this is a connect request, and we can present a username signature, send it along if (!_domainHandler.isConnected()) { DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo(); packetStream << accountInfo.getUsername(); const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature(); if (!usernameSignature.isEmpty()) { qDebug() << "Including username signature in domain connect request."; packetStream << usernameSignature; } } if (!isUsingDTLS) { writeDatagram(domainServerPacket, _domainHandler.getSockAddr(), QUuid()); } const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; static unsigned int numDomainCheckins = 0; // send a STUN request every Nth domain server check in so we update our public socket, if required if (numDomainCheckins++ % NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST == 0) { sendSTUNRequest(); } if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS // so emit our signal that says that emit limitOfSilentDomainCheckInsReached(); } // increment the count of un-replied check-ins _numNoReplyDomainCheckIns++; } }
void ScriptEngine::run() { if (!_isInitialized) { init(); } _isRunning = true; emit runningStateChanged(); QScriptValue result = _engine.evaluate(_scriptContents); if (_engine.hasUncaughtException()) { int line = _engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << result.toString(); emit errorMessage("Uncaught exception at line" + QString::number(line) + ":" + result.toString()); } QElapsedTimer startTime; startTime.start(); int thisFrame = 0; NodeList* nodeList = NodeList::getInstance(); qint64 lastUpdate = usecTimestampNow(); while (!_isFinished) { int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec if (usecToSleep > 0) { usleep(usecToSleep); } if (_isFinished) { break; } QCoreApplication::processEvents(); if (_isFinished) { break; } if (_voxelsScriptingInterface.getVoxelPacketSender()->serversExist()) { // release the queue of edit voxel messages. _voxelsScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent if (!_voxelsScriptingInterface.getVoxelPacketSender()->isThreaded()) { _voxelsScriptingInterface.getVoxelPacketSender()->process(); } } if (_particlesScriptingInterface.getParticlePacketSender()->serversExist()) { // release the queue of edit voxel messages. _particlesScriptingInterface.getParticlePacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent if (!_particlesScriptingInterface.getParticlePacketSender()->isThreaded()) { _particlesScriptingInterface.getParticlePacketSender()->process(); } } if (_modelsScriptingInterface.getModelPacketSender()->serversExist()) { // release the queue of edit voxel messages. _modelsScriptingInterface.getModelPacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent if (!_modelsScriptingInterface.getModelPacketSender()->isThreaded()) { _modelsScriptingInterface.getModelPacketSender()->process(); } } if (_isAvatar && _avatarData) { const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * SAMPLE_RATE) / (1000 * 1000)) + 0.5); const int SCRIPT_AUDIO_BUFFER_BYTES = SCRIPT_AUDIO_BUFFER_SAMPLES * sizeof(int16_t); QByteArray avatarPacket = byteArrayWithPopulatedHeader(PacketTypeAvatarData); avatarPacket.append(_avatarData->toByteArray()); nodeList->broadcastToNodes(avatarPacket, NodeSet() << NodeType::AvatarMixer); if (_isListeningToAudioStream || _avatarSound) { // if we have an avatar audio stream then send it out to our audio-mixer bool silentFrame = true; int16_t numAvailableSamples = SCRIPT_AUDIO_BUFFER_SAMPLES; const int16_t* nextSoundOutput = NULL; if (_avatarSound) { const QByteArray& soundByteArray = _avatarSound->getByteArray(); nextSoundOutput = reinterpret_cast<const int16_t*>(soundByteArray.data() + _numAvatarSoundSentBytes); int numAvailableBytes = (soundByteArray.size() - _numAvatarSoundSentBytes) > SCRIPT_AUDIO_BUFFER_BYTES ? SCRIPT_AUDIO_BUFFER_BYTES : soundByteArray.size() - _numAvatarSoundSentBytes; numAvailableSamples = numAvailableBytes / sizeof(int16_t); // check if the all of the _numAvatarAudioBufferSamples to be sent are silence for (int i = 0; i < numAvailableSamples; ++i) { if (nextSoundOutput[i] != 0) { silentFrame = false; break; } } _numAvatarSoundSentBytes += numAvailableBytes; if (_numAvatarSoundSentBytes == soundByteArray.size()) { // we're done with this sound object - so set our pointer back to NULL // and our sent bytes back to zero _avatarSound = NULL; _numAvatarSoundSentBytes = 0; } } QByteArray audioPacket = byteArrayWithPopulatedHeader(silentFrame ? PacketTypeSilentAudioFrame : PacketTypeMicrophoneAudioNoEcho); QDataStream packetStream(&audioPacket, QIODevice::Append); // use the orientation and position of this avatar for the source of this audio packetStream.writeRawData(reinterpret_cast<const char*>(&_avatarData->getPosition()), sizeof(glm::vec3)); glm::quat headOrientation = _avatarData->getHeadOrientation(); packetStream.writeRawData(reinterpret_cast<const char*>(&headOrientation), sizeof(glm::quat)); if (silentFrame) { if (!_isListeningToAudioStream) { // if we have a silent frame and we're not listening then just send nothing and break out of here break; } // write the number of silent samples so the audio-mixer can uphold timing packetStream.writeRawData(reinterpret_cast<const char*>(&SCRIPT_AUDIO_BUFFER_SAMPLES), sizeof(int16_t)); } else if (nextSoundOutput) { // write the raw audio data packetStream.writeRawData(reinterpret_cast<const char*>(nextSoundOutput), numAvailableSamples * sizeof(int16_t)); } nodeList->broadcastToNodes(audioPacket, NodeSet() << NodeType::AudioMixer); } } qint64 now = usecTimestampNow(); float deltaTime = (float) (now - lastUpdate) / (float) USECS_PER_SECOND; emit update(deltaTime); lastUpdate = now; if (_engine.hasUncaughtException()) { int line = _engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << _engine.uncaughtException().toString(); emit errorMessage("Uncaught exception at line" + QString::number(line) + ":" + _engine.uncaughtException().toString()); } } emit scriptEnding(); // kill the avatar identity timer delete _avatarIdentityTimer; if (_voxelsScriptingInterface.getVoxelPacketSender()->serversExist()) { // release the queue of edit voxel messages. _voxelsScriptingInterface.getVoxelPacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent if (!_voxelsScriptingInterface.getVoxelPacketSender()->isThreaded()) { _voxelsScriptingInterface.getVoxelPacketSender()->process(); } } if (_particlesScriptingInterface.getParticlePacketSender()->serversExist()) { // release the queue of edit voxel messages. _particlesScriptingInterface.getParticlePacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent if (!_particlesScriptingInterface.getParticlePacketSender()->isThreaded()) { _particlesScriptingInterface.getParticlePacketSender()->process(); } } if (_modelsScriptingInterface.getModelPacketSender()->serversExist()) { // release the queue of edit voxel messages. _modelsScriptingInterface.getModelPacketSender()->releaseQueuedMessages(); // since we're in non-threaded mode, call process so that the packets are sent if (!_modelsScriptingInterface.getModelPacketSender()->isThreaded()) { _modelsScriptingInterface.getModelPacketSender()->process(); } } // If we were on a thread, then wait till it's done if (thread()) { thread()->quit(); } emit finished(_fileNameString); _isRunning = false; emit runningStateChanged(); }
void DatagramProcessor::processDatagrams() { if (_isShuttingDown) { return; // bail early... we're shutting down. } PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "DatagramProcessor::processDatagrams()"); HifiSockAddr senderSockAddr; static QByteArray incomingPacket; Application* application = Application::getInstance(); auto nodeList = DependencyManager::get<NodeList>(); while (DependencyManager::get<NodeList>()->getNodeSocket().hasPendingDatagrams()) { incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->readDatagram(incomingPacket, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); _inPacketCount++; _inByteCount += incomingPacket.size(); if (nodeList->packetVersionAndHashMatch(incomingPacket)) { PacketType incomingType = packetTypeForPacket(incomingPacket); // only process this packet if we have a match on the packet version switch (incomingType) { case PacketTypeAudioEnvironment: case PacketTypeAudioStreamStats: case PacketTypeMixedAudio: case PacketTypeSilentAudioFrame: { if (incomingType == PacketTypeAudioStreamStats) { QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioStreamStatsPacket", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else if (incomingType == PacketTypeAudioEnvironment) { QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "parseAudioEnvironmentData", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else { QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "addReceivedAudioToStream", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } // update having heard from the audio-mixer and record the bytes received SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket); if (audioMixer) { audioMixer->setLastHeardMicrostamp(usecTimestampNow()); } break; } case PacketTypeEntityData: case PacketTypeEntityErase: case PacketTypeOctreeStats: case PacketTypeEnvironmentData: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _octreeProcessor.queueReceivedPacket()"); SharedNodePointer matchedNode = DependencyManager::get<NodeList>()->sendingNodeForPacket(incomingPacket); if (matchedNode) { // add this packet to our list of octree packets and process them on the octree data processing application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket); } break; } case PacketTypeBulkAvatarData: case PacketTypeKillAvatar: case PacketTypeAvatarIdentity: case PacketTypeAvatarBillboard: { // update having heard from the avatar-mixer and record the bytes received SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket); if (avatarMixer) { avatarMixer->setLastHeardMicrostamp(usecTimestampNow()); QMetaObject::invokeMethod(DependencyManager::get<AvatarManager>().data(), "processAvatarMixerDatagram", Q_ARG(const QByteArray&, incomingPacket), Q_ARG(const QWeakPointer<Node>&, avatarMixer)); } break; } case PacketTypeDomainConnectionDenied: { int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainConnectionDenied); QDataStream packetStream(QByteArray(incomingPacket.constData() + headerSize, incomingPacket.size() - headerSize)); QString reason; packetStream >> reason; // output to the log so the user knows they got a denied connection request // and check and signal for an access token so that we can make sure they are logged in qCDebug(interfaceapp) << "The domain-server denied a connection request: " << reason; qCDebug(interfaceapp) << "You may need to re-log to generate a keypair so you can provide a username signature."; application->domainConnectionDenied(reason); AccountManager::getInstance().checkAndSignalForAccessToken(); break; } case PacketTypeNoisyMute: case PacketTypeMuteEnvironment: { bool mute = !DependencyManager::get<AudioClient>()->isMuted(); if (incomingType == PacketTypeMuteEnvironment) { glm::vec3 position; float radius; int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment); memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3)); memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float)); float distance = glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(), position); mute = mute && (distance < radius); } if (mute) { DependencyManager::get<AudioClient>()->toggleMute(); if (incomingType == PacketTypeMuteEnvironment) { AudioScriptingInterface::getInstance().environmentMuted(); } else { AudioScriptingInterface::getInstance().mutedByMixer(); } } break; } case PacketTypeEntityEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_entityEditSender.processNackPacket(incomingPacket); } break; default: nodeList->processNodeData(senderSockAddr, incomingPacket); break; } } }