bool AssetClient::uploadAsset(const QByteArray& data, const QString& extension, UploadResultCallback callback) { auto nodeList = DependencyManager::get<NodeList>(); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); if (assetServer) { auto packetList = NLPacketList::create(PacketType::AssetUpload, QByteArray(), true, true); auto messageID = ++_currentID; packetList->writePrimitive(messageID); packetList->writePrimitive(static_cast<uint8_t>(extension.length())); packetList->write(extension.toLatin1().constData(), extension.length()); uint64_t size = data.length(); packetList->writePrimitive(size); packetList->write(data.constData(), size); nodeList->sendPacketList(std::move(packetList), *assetServer); _pendingUploads[assetServer][messageID] = callback; return true; } return false; }
void AudioMixer::handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) { auto nodeList = DependencyManager::get<NodeList>(); if (sendingNode->isAllowedEditor()) { glm::vec3 position; float radius; auto newPacket = NLPacket::create(PacketType::MuteEnvironment, sizeof(position) + sizeof(radius)); // read the position and radius from the sent packet message->readPrimitive(&position); message->readPrimitive(&radius); // write them to our packet newPacket->writePrimitive(position); newPacket->writePrimitive(radius); nodeList->eachNode([&](const SharedNodePointer& node){ if (node->getType() == NodeType::Agent && node->getActiveSocket() && node->getLinkedData() && node != sendingNode) { nodeList->sendUnreliablePacket(*newPacket, *node); } }); } }
void sendEnvironmentPacket(const SharedNodePointer& node, AudioMixerClientData& data) { bool hasReverb = false; float reverbTime, wetLevel; auto& reverbSettings = AudioMixer::getReverbSettings(); auto& audioZones = AudioMixer::getAudioZones(); AvatarAudioStream* stream = data.getAvatarAudioStream(); glm::vec3 streamPosition = stream->getPosition(); // find reverb properties for (int i = 0; i < reverbSettings.size(); ++i) { AABox box = audioZones[reverbSettings[i].zone]; if (box.contains(streamPosition)) { hasReverb = true; reverbTime = reverbSettings[i].reverbTime; wetLevel = reverbSettings[i].wetLevel; break; } } // check if data changed bool dataChanged = (stream->hasReverb() != hasReverb) || (stream->hasReverb() && (stream->getRevebTime() != reverbTime || stream->getWetLevel() != wetLevel)); if (dataChanged) { // update stream if (hasReverb) { stream->setReverb(reverbTime, wetLevel); } else { stream->clearReverb(); } } // send packet at change or every so often float CHANCE_OF_SEND = 0.01f; bool sendData = dataChanged || (randFloat() < CHANCE_OF_SEND); if (sendData) { // size the packet unsigned char bitset = 0; int packetSize = sizeof(bitset); if (hasReverb) { packetSize += sizeof(reverbTime) + sizeof(wetLevel); } // write the packet auto envPacket = NLPacket::create(PacketType::AudioEnvironment, packetSize); if (hasReverb) { setAtBit(bitset, HAS_REVERB_BIT); } envPacket->writePrimitive(bitset); if (hasReverb) { envPacket->writePrimitive(reverbTime); envPacket->writePrimitive(wetLevel); } // send the packet DependencyManager::get<NodeList>()->sendPacket(std::move(envPacket), *node); } }
void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) { if (senderNode->getCanRez()) { qDebug() << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID()); auto task = new UploadAssetTask(packetList, senderNode, _resourcesDirectory); _taskPool.start(task); } else { // this is a node the domain told us is not allowed to rez entities // for now this also means it isn't allowed to add assets // so return a packet with error that indicates that auto permissionErrorPacket = NLPacket::create(PacketType::AssetUploadReply, sizeof(MessageID) + sizeof(AssetServerError)); MessageID messageID; packetList->readPrimitive(&messageID); // write the message ID and a permission denied error permissionErrorPacket->writePrimitive(messageID); permissionErrorPacket->writePrimitive(AssetServerError::PermissionDenied); // send off the packet auto nodeList = DependencyManager::get<NodeList>(); nodeList->sendPacket(std::move(permissionErrorPacket), *senderNode); } }
void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode) { auto nodeList = DependencyManager::get<NodeList>(); // The append flag is a boolean value that will be packed right after the header. // This flag allows the client to know when it has received all stats packets, so it can group any downstream effects, // and clear its cache of injector stream stats; it helps to prevent buildup of dead audio stream stats in the client. quint8 appendFlag = AudioStreamStats::START; auto streamsCopy = getAudioStreams(); // pack and send stream stats packets until all audio streams' stats are sent int numStreamStatsRemaining = int(streamsCopy.size()); auto it = streamsCopy.cbegin(); while (numStreamStatsRemaining > 0) { auto statsPacket = NLPacket::create(PacketType::AudioStreamStats); int numStreamStatsRoomFor = (int)(statsPacket->size() - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats); // calculate the number of stream stats to follow quint16 numStreamStatsToPack = std::min(numStreamStatsRemaining, numStreamStatsRoomFor); // is this the terminal packet? if (numStreamStatsRemaining <= numStreamStatsToPack) { appendFlag |= AudioStreamStats::END; } // pack the append flag in this packet statsPacket->writePrimitive(appendFlag); appendFlag = 0; // pack the number of stream stats to follow statsPacket->writePrimitive(numStreamStatsToPack); // pack the calculated number of stream stats for (int i = 0; i < numStreamStatsToPack; i++) { PositionalAudioStream* stream = it->second.get(); stream->perSecondCallbackForUpdatingStats(); AudioStreamStats streamStats = stream->getAudioStreamStats(); statsPacket->writePrimitive(streamStats); ++it; } numStreamStatsRemaining -= numStreamStatsToPack; // send the current packet nodeList->sendPacket(std::move(statsPacket), *destinationNode); } }
void AudioMixerClientData::sendAudioStreamStatsPackets(const SharedNodePointer& destinationNode) { // since audio stream stats packets are sent periodically, this is a good place to remove our dead injected streams. removeDeadInjectedStreams(); auto nodeList = DependencyManager::get<NodeList>(); // The append flag is a boolean value that will be packed right after the header. The first packet sent // inside this method will have 0 for this flag, while every subsequent packet will have 1 for this flag. // The sole purpose of this flag is so the client can clear its map of injected audio stream stats when // it receives a packet with an appendFlag of 0. This prevents the buildup of dead audio stream stats in the client. quint8 appendFlag = 0; // pack and send stream stats packets until all audio streams' stats are sent int numStreamStatsRemaining = _audioStreams.size(); QHash<QUuid, PositionalAudioStream*>::ConstIterator audioStreamsIterator = _audioStreams.constBegin(); while (numStreamStatsRemaining > 0) { auto statsPacket = NLPacket::create(PacketType::AudioStreamStats); // pack the append flag in this packet statsPacket->writePrimitive(appendFlag); appendFlag = 1; int numStreamStatsRoomFor = (statsPacket->size() - sizeof(quint8) - sizeof(quint16)) / sizeof(AudioStreamStats); // calculate and pack the number of stream stats to follow quint16 numStreamStatsToPack = std::min(numStreamStatsRemaining, numStreamStatsRoomFor); statsPacket->writePrimitive(numStreamStatsToPack); // pack the calculated number of stream stats for (int i = 0; i < numStreamStatsToPack; i++) { PositionalAudioStream* stream = audioStreamsIterator.value(); stream->perSecondCallbackForUpdatingStats(); AudioStreamStats streamStats = stream->getAudioStreamStats(); statsPacket->writePrimitive(streamStats); audioStreamsIterator++; } numStreamStatsRemaining -= numStreamStatsToPack; // send the current packet nodeList->sendPacket(std::move(statsPacket), *destinationNode); } }
void NodeList::sendDSPathQuery(const QString& newPath) { // only send a path query if we know who our DS is or is going to be if (_domainHandler.isSocketKnown()) { // construct the path query packet auto pathQueryPacket = NLPacket::create(PacketType::DomainServerPathQuery); // get the UTF8 representation of path query QByteArray pathQueryUTF8 = newPath.toUtf8(); // get the size of the UTF8 representation of the desired path quint16 numPathBytes = pathQueryUTF8.size(); if (numPathBytes + ((qint16) sizeof(numPathBytes)) < pathQueryPacket->bytesAvailableForWrite()) { // append the size of the path to the query packet pathQueryPacket->writePrimitive(numPathBytes); // append the path itself to the query packet pathQueryPacket->write(pathQueryUTF8); qCDebug(networking) << "Sending a path query packet for path" << newPath << "to domain-server at" << _domainHandler.getSockAddr(); // send off the path query sendPacket(std::move(pathQueryPacket), _domainHandler.getSockAddr()); } else { qCDebug(networking) << "Path" << newPath << "would make PacketType::DomainServerPathQuery packet > MAX_PACKET_SIZE." << "Will not send query."; } } }
void AvatarMixerClientData::ignoreOther(const Node* self, const Node* other) { if (!isRadiusIgnoring(other->getUUID())) { addToRadiusIgnoringSet(other->getUUID()); auto killPacket = NLPacket::create(PacketType::KillAvatar, NUM_BYTES_RFC4122_UUID + sizeof(KillAvatarReason), true); killPacket->write(other->getUUID().toRfc4122()); if (_isIgnoreRadiusEnabled) { killPacket->writePrimitive(KillAvatarReason::TheirAvatarEnteredYourBubble); } else { killPacket->writePrimitive(KillAvatarReason::YourAvatarEnteredTheirBubble); } setLastBroadcastTime(other->getLocalID(), 0); resetSentTraitData(other->getLocalID()); DependencyManager::get<NodeList>()->sendPacket(std::move(killPacket), *self); } }
void XspfDataWriter::writeTitle() { assert(this->d->data != NULL); XML_Char const * const title = this->d->data->getTitle(); if (title != NULL) { writePrimitive(_PT("title"), title); } }
void XspfDataWriter::writeCreator() { assert(this->d->data != NULL); XML_Char const * const creator = this->d->data->getCreator(); if (creator != NULL) { writePrimitive(_PT("creator"), creator); } }
void XspfDataWriter::writeAnnotation() { assert(this->d->data != NULL); XML_Char const * const annotation = this->d->data->getAnnotation(); if (annotation != NULL) { writePrimitive(_PT("annotation"), annotation); } }
void XspfDataWriter::writeInfo() { assert(this->d->data != NULL); XML_Char const * const info = this->d->data->getInfo(); if (info != NULL) { XML_Char * const relUri = makeRelativeUri(info); writePrimitive(_PT("info"), relUri); delete [] relUri; } }
void XspfDataWriter::writeImage() { assert(this->d->data != NULL); XML_Char const * const image = this->d->data->getImage(); if (image != NULL) { XML_Char * const relUri = makeRelativeUri(image); writePrimitive(_PT("image"), relUri); delete [] relUri; } }
void MessagesMixer::handleMessages(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) { Q_ASSERT(packetList->getType() == PacketType::MessagesData); QByteArray packetData = packetList->getMessage(); QBuffer packet{ &packetData }; packet.open(QIODevice::ReadOnly); quint16 channelLength; packet.read(reinterpret_cast<char*>(&channelLength), sizeof(channelLength)); auto channelData = packet.read(channelLength); QString channel = QString::fromUtf8(channelData); quint16 messageLength; packet.read(reinterpret_cast<char*>(&messageLength), sizeof(messageLength)); auto messageData = packet.read(messageLength); QString message = QString::fromUtf8(messageData); auto nodeList = DependencyManager::get<NodeList>(); nodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { return node->getType() == NodeType::Agent && node->getActiveSocket() && _channelSubscribers[channel].contains(node->getUUID()); }, [&](const SharedNodePointer& node) { auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); auto channelUtf8 = channel.toUtf8(); quint16 channelLength = channelUtf8.length(); packetList->writePrimitive(channelLength); packetList->write(channelUtf8); auto messageUtf8 = message.toUtf8(); quint16 messageLength = messageUtf8.length(); packetList->writePrimitive(messageLength); packetList->write(messageUtf8); nodeList->sendPacketList(std::move(packetList), *node); }); }
void MessagesClient::sendMessage(const QString& channel, const QString& message) { auto nodeList = DependencyManager::get<NodeList>(); SharedNodePointer messagesMixer = nodeList->soloNodeOfType(NodeType::MessagesMixer); if (messagesMixer) { auto packetList = NLPacketList::create(PacketType::MessagesData, QByteArray(), true, true); auto channelUtf8 = channel.toUtf8(); quint16 channelLength = channelUtf8.length(); packetList->writePrimitive(channelLength); packetList->write(channelUtf8); auto messageUtf8 = message.toUtf8(); quint16 messageLength = messageUtf8.length(); packetList->writePrimitive(messageLength); packetList->write(messageUtf8); nodeList->sendPacketList(std::move(packetList), *messagesMixer); } }
bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end, ReceivedAssetCallback callback, ProgressCallback progressCallback) { if (hash.length() != SHA256_HASH_HEX_LENGTH) { qCWarning(asset_client) << "Invalid hash size"; return false; } auto nodeList = DependencyManager::get<NodeList>(); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); if (assetServer) { auto messageID = ++_currentID; auto payloadSize = sizeof(messageID) + SHA256_HASH_LENGTH + sizeof(uint8_t) + extension.length() + sizeof(start) + sizeof(end); auto packet = NLPacket::create(PacketType::AssetGet, payloadSize, true); qCDebug(asset_client) << "Requesting data from" << start << "to" << end << "of" << hash << "from asset-server."; packet->writePrimitive(messageID); packet->write(QByteArray::fromHex(hash.toLatin1())); packet->writePrimitive(uint8_t(extension.length())); packet->write(extension.toLatin1()); packet->writePrimitive(start); packet->writePrimitive(end); nodeList->sendPacket(std::move(packet), *assetServer); _pendingRequests[assetServer][messageID] = { callback, progressCallback }; return true; } return false; }
void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) { if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) { MessageID messageID; message->readPrimitive(&messageID); auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID)); auto replyPacketList = NLPacketList::create(PacketType::EntityScriptGetStatusReply, QByteArray(), true, true); replyPacketList->writePrimitive(messageID); EntityScriptDetails details; if (_entitiesScriptEngine->getEntityScriptDetails(entityID, details)) { replyPacketList->writePrimitive(true); replyPacketList->writePrimitive(details.status); replyPacketList->writeString(details.errorInfo); } else { replyPacketList->writePrimitive(false); } auto nodeList = DependencyManager::get<NodeList>(); nodeList->sendPacketList(std::move(replyPacketList), *senderNode); } }
void sendSilentPacket(const SharedNodePointer& node, AudioMixerClientData& data) { const int SILENT_PACKET_SIZE = sizeof(quint16) + AudioConstants::MAX_CODEC_NAME_LENGTH_ON_WIRE + sizeof(quint16); quint16 sequence = data.getOutgoingSequenceNumber(); QString codec = data.getCodecName(); auto mixPacket = createAudioPacket(PacketType::SilentAudioFrame, SILENT_PACKET_SIZE, sequence, codec); // pack number of samples mixPacket->writePrimitive(AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); // send packet DependencyManager::get<NodeList>()->sendPacket(std::move(mixPacket), *node); data.incrementOutgoingMixedAudioSequenceNumber(); }
void AssetServer::handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) { QByteArray assetHash; MessageID messageID; uint8_t extensionLength; if (packet->getPayloadSize() < qint64(SHA256_HASH_LENGTH + sizeof(messageID) + sizeof(extensionLength))) { qDebug() << "ERROR bad file request"; return; } packet->readPrimitive(&messageID); assetHash = packet->readWithoutCopy(SHA256_HASH_LENGTH); packet->readPrimitive(&extensionLength); QByteArray extension = packet->read(extensionLength); auto replyPacket = NLPacket::create(PacketType::AssetGetInfoReply); QByteArray hexHash = assetHash.toHex(); replyPacket->writePrimitive(messageID); replyPacket->write(assetHash); QString fileName = QString(hexHash) + "." + extension; QFileInfo fileInfo { _resourcesDirectory.filePath(fileName) }; if (fileInfo.exists() && fileInfo.isReadable()) { qDebug() << "Opening file: " << fileInfo.filePath(); replyPacket->writePrimitive(AssetServerError::NoError); replyPacket->writePrimitive(fileInfo.size()); } else { qDebug() << "Asset not found: " << QString(hexHash); replyPacket->writePrimitive(AssetServerError::AssetNotFound); } auto nodeList = DependencyManager::get<NodeList>(); nodeList->sendPacket(std::move(replyPacket), *senderNode); }
void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale, PacketType packetType, QString codecName) { static std::mutex _mutex; using Locker = std::unique_lock<std::mutex>; auto nodeList = DependencyManager::get<NodeList>(); SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); if (audioMixer && audioMixer->getActiveSocket()) { Locker lock(_mutex); auto audioPacket = NLPacket::create(packetType); // FIXME - this is not a good way to determine stereoness with codecs.... quint8 isStereo = bytes == AudioConstants::NETWORK_FRAME_BYTES_STEREO ? 1 : 0; // write sequence number auto sequence = sequenceNumber++; audioPacket->writePrimitive(sequence); // write the codec audioPacket->writeString(codecName); if (packetType == PacketType::SilentAudioFrame) { // pack num silent samples quint16 numSilentSamples = isStereo ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; audioPacket->writePrimitive(numSilentSamples); } else { // set the mono/stereo byte audioPacket->writePrimitive(isStereo); } // pack the three float positions audioPacket->writePrimitive(transform.getTranslation()); // pack the orientation audioPacket->writePrimitive(transform.getRotation()); audioPacket->writePrimitive(avatarBoundingBoxCorner); audioPacket->writePrimitive(avatarBoundingBoxScale); if (audioPacket->getType() != PacketType::SilentAudioFrame) { // audio samples have already been packed (written to networkAudioSamples) int leadingBytes = audioPacket->getPayloadSize(); audioPacket->setPayloadSize(leadingBytes + bytes); memcpy(audioPacket->getPayload() + leadingBytes, audioData, bytes); } nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket); nodeList->sendUnreliablePacket(*audioPacket, *audioMixer); } }
void EntityScriptServer::handleEntityScriptGetStatusPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) { // These are temporary checks until we can ensure that nodes eventually disconnect if the Domain Server stops telling them // about each other. if (senderNode->getCanRez() || senderNode->getCanRezTmp() || senderNode->getCanRezCertified() || senderNode->getCanRezTmpCertified()) { MessageID messageID; message->readPrimitive(&messageID); auto entityID = QUuid::fromRfc4122(message->read(NUM_BYTES_RFC4122_UUID)); auto replyPacketList = NLPacketList::create(PacketType::EntityScriptGetStatusReply, QByteArray(), true, true); replyPacketList->writePrimitive(messageID); EntityScriptDetails details; if (_entitiesScriptEngine->getEntityScriptDetails(entityID, details)) { replyPacketList->writePrimitive(true); replyPacketList->writePrimitive(details.status); replyPacketList->writeString(details.errorInfo); } else { replyPacketList->writePrimitive(false); } auto nodeList = DependencyManager::get<NodeList>(); nodeList->sendPacketList(std::move(replyPacketList), *senderNode); } }
bool AssetClient::getAssetInfo(const QString& hash, const QString& extension, GetInfoCallback callback) { auto nodeList = DependencyManager::get<NodeList>(); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); if (assetServer) { auto messageID = ++_currentID; auto payloadSize = sizeof(messageID) + SHA256_HASH_LENGTH + sizeof(uint8_t) + extension.length(); auto packet = NLPacket::create(PacketType::AssetGetInfo, payloadSize, true); packet->writePrimitive(messageID); packet->write(QByteArray::fromHex(hash.toLatin1())); packet->writePrimitive(uint8_t(extension.length())); packet->write(extension.toLatin1()); nodeList->sendPacket(std::move(packet), *assetServer); _pendingInfoRequests[assetServer][messageID] = callback; return true; } return false; }
void AssignmentClient::sendStatusPacketToACM() { // tell the assignment client monitor what this assignment client is doing (if anything) auto nodeList = DependencyManager::get<NodeList>(); quint8 assignmentType = Assignment::Type::AllTypes; if (_currentAssignment) { assignmentType = _currentAssignment->getType(); } auto statusPacket = NLPacket::create(PacketType::AssignmentClientStatus, sizeof(assignmentType) + NUM_BYTES_RFC4122_UUID); statusPacket->write(_childAssignmentUUID.toRfc4122()); statusPacket->writePrimitive(assignmentType); nodeList->sendPacket(std::move(statusPacket), _assignmentClientMonitorSocket); }
void EntityScriptServer::negotiateAudioFormat() { auto nodeList = DependencyManager::get<NodeList>(); auto negotiateFormatPacket = NLPacket::create(PacketType::NegotiateAudioFormat); auto codecPlugins = PluginManager::getInstance()->getCodecPlugins(); quint8 numberOfCodecs = (quint8)codecPlugins.size(); negotiateFormatPacket->writePrimitive(numberOfCodecs); for (auto& plugin : codecPlugins) { auto codecName = plugin->getName(); negotiateFormatPacket->writeString(codecName); } // grab our audio mixer from the NodeList, if it exists SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); if (audioMixer) { // send off this mute packet nodeList->sendPacket(std::move(negotiateFormatPacket), *audioMixer); } }
void Wallet::sendChallengeOwnershipResponses() { if (_pendingChallenges.size() == 0 || getSalt().length() == 0) { return; } auto nodeList = DependencyManager::get<NodeList>(); EC_KEY* ec = readKeys(keyFilePath()); for (const auto& packet: _pendingChallenges) { // With EC keys, we receive a nonce from the metaverse server, which is signed // here with the private key and returned. Verification is done at server. QString sig; bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest; int status; int idByteArraySize; int textByteArraySize; int challengingNodeUUIDByteArraySize; packet->readPrimitive(&idByteArraySize); packet->readPrimitive(&textByteArraySize); // returns a cast char*, size if (challengeOriginatedFromClient) { packet->readPrimitive(&challengingNodeUUIDByteArraySize); } // "encryptedText" is now a series of random bytes, a nonce QByteArray id = packet->read(idByteArraySize); QByteArray text = packet->read(textByteArraySize); QByteArray challengingNodeUUID; if (challengeOriginatedFromClient) { challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize); } if (ec) { ERR_clear_error(); sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with status = 1; } else { qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed."; status = -1; } QByteArray textByteArray; if (status > -1) { textByteArray = sig.toUtf8(); } textByteArraySize = textByteArray.size(); int idSize = id.size(); // setup the packet const SharedNodePointer sendingNode = nodeList->nodeWithLocalID(packet->getSourceID()); if (!sendingNode.isNull()) { if (challengeOriginatedFromClient) { auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), true); textPacket->writePrimitive(idSize); textPacket->writePrimitive(textByteArraySize); textPacket->writePrimitive(challengingNodeUUIDByteArraySize); textPacket->write(id); textPacket->write(textByteArray); textPacket->write(challengingNodeUUID); qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for id" << id; nodeList->sendPacket(std::move(textPacket), *sendingNode); } else { auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true); textPacket->writePrimitive(idSize); textPacket->writePrimitive(textByteArraySize); textPacket->write(id); textPacket->write(textByteArray); qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id; nodeList->sendPacket(std::move(textPacket), *sendingNode); } } else { qCDebug(commerce) << "Challenging Node Local ID" << packet->getSourceID() << "disconnected before response"; } if (status == -1) { qCDebug(commerce) << "During entity ownership challenge, signing the text failed."; long error = ERR_get_error(); if (error != 0) { const char* error_str = ERR_error_string(error, NULL); qCWarning(entities) << "EC error:" << error_str; } } } EC_KEY_free(ec); _pendingChallenges.clear(); }
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 avatarByteArray = _avatarData->toByteArray(); auto avatarPacket = NLPacket::create(PacketType::AvatarData, avatarByteArray.size()); avatarPacket->write(avatarByteArray); nodeList->broadcastToNodes(std::move(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; } } auto audioPacket = NLPacket::create(silentFrame ? PacketType::SilentAudioFrame : PacketType::MicrophoneAudioNoEcho); // seek past the sequence number, will be packed when destination node is known audioPacket->seek(sizeof(quint16)); 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 audioPacket->writePrimitive(SCRIPT_AUDIO_BUFFER_SAMPLES); // use the orientation and position of this avatar for the source of this audio audioPacket->writePrimitive(_avatarData->getPosition()); glm::quat headOrientation = _avatarData->getHeadOrientation(); audioPacket->writePrimitive(headOrientation); } else if (nextSoundOutput) { // assume scripted avatar audio is mono and set channel flag to zero audioPacket->writePrimitive((quint8) 0); // use the orientation and position of this avatar for the source of this audio audioPacket->writePrimitive(_avatarData->getPosition()); glm::quat headOrientation = _avatarData->getHeadOrientation(); audioPacket->writePrimitive(headOrientation); // write the raw audio data audioPacket->write(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](const SharedNodePointer& node){ // only send to nodes of type AudioMixer if (node->getType() == NodeType::AudioMixer) { // pack sequence number quint16 sequence = _outgoingScriptAudioSequenceNumbers[node->getUUID()]++; audioPacket->seek(0); audioPacket->writePrimitive(sequence); // send audio packet nodeList->sendUnreliablePacket(*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()) { // wait here till the edit packet sender is completely done sending while (entityScriptingInterface->getEntityPacketSender()->hasPacketsToSend()) { entityScriptingInterface->getEntityPacketSender()->process(); QCoreApplication::processEvents(); } } else { // FIXME - do we need to have a similar "wait here" loop for non-threaded packet senders? } } emit finished(_fileNameString); _isRunning = false; emit runningStateChanged(); emit doneRunning(); _doneRunningThisScript = true; }
void UploadAssetTask::run() { auto data = _receivedMessage->getMessage(); QBuffer buffer { &data }; buffer.open(QIODevice::ReadOnly); MessageID messageID; buffer.read(reinterpret_cast<char*>(&messageID), sizeof(messageID)); uint64_t fileSize; buffer.read(reinterpret_cast<char*>(&fileSize), sizeof(fileSize)); qDebug() << "UploadAssetTask reading a file of " << fileSize << "bytes from" << uuidStringWithoutCurlyBraces(_senderNode->getUUID()); auto replyPacket = NLPacket::create(PacketType::AssetUploadReply); replyPacket->writePrimitive(messageID); if (fileSize > MAX_UPLOAD_SIZE) { replyPacket->writePrimitive(AssetServerError::AssetTooLarge); } else { QByteArray fileData = buffer.read(fileSize); auto hash = hashData(fileData); auto hexHash = hash.toHex(); qDebug() << "Hash for uploaded file from" << uuidStringWithoutCurlyBraces(_senderNode->getUUID()) << "is: (" << hexHash << ") "; QFile file { _resourcesDir.filePath(QString(hexHash)) }; bool existingCorrectFile = false; if (file.exists()) { // check if the local file has the correct contents, otherwise we overwrite if (file.open(QIODevice::ReadOnly) && hashData(file.readAll()) == hash) { qDebug() << "Not overwriting existing verified file: " << hexHash; existingCorrectFile = true; replyPacket->writePrimitive(AssetServerError::NoError); replyPacket->write(hash); } else { qDebug() << "Overwriting an existing file whose contents did not match the expected hash: " << hexHash; file.close(); } } if (!existingCorrectFile) { if (file.open(QIODevice::WriteOnly) && file.write(fileData) == qint64(fileSize)) { qDebug() << "Wrote file" << hexHash << "to disk. Upload complete"; file.close(); replyPacket->writePrimitive(AssetServerError::NoError); replyPacket->write(hash); } else { qWarning() << "Failed to upload or write to file" << hexHash << " - upload failed."; // upload has failed - remove the file and return an error auto removed = file.remove(); if (!removed) { qWarning() << "Removal of failed upload file" << hexHash << "failed."; } replyPacket->writePrimitive(AssetServerError::FileOperationFailed); } } } auto nodeList = DependencyManager::get<NodeList>(); nodeList->sendPacket(std::move(replyPacket), *_senderNode); }
void AudioInjector::injectToMixer() { if (_currentSendOffset < 0 || _currentSendOffset >= _audioData.size()) { _currentSendOffset = 0; } auto nodeList = DependencyManager::get<NodeList>(); // make sure we actually have samples downloaded to inject if (_audioData.size()) { auto audioPacket = NLPacket::create(PacketType::InjectAudio); // setup the packet for injected audio QDataStream audioPacketStream(audioPacket.get()); // pack some placeholder sequence number for now audioPacketStream << (quint16) 0; // pack stream identifier (a generated UUID) audioPacketStream << QUuid::createUuid(); // pack the stereo/mono type of the stream audioPacketStream << _options.stereo; // pack the flag for loopback uchar loopbackFlag = (uchar) true; audioPacketStream << loopbackFlag; // pack the position for injected audio int positionOptionOffset = audioPacket->pos(); audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_options.position), sizeof(_options.position)); // pack our orientation for injected audio audioPacketStream.writeRawData(reinterpret_cast<const char*>(&_options.orientation), sizeof(_options.orientation)); // pack zero for radius float radius = 0; audioPacketStream << radius; // pack 255 for attenuation byte int volumeOptionOffset = audioPacket->pos(); quint8 volume = MAX_INJECTOR_VOLUME * _options.volume; audioPacketStream << volume; audioPacketStream << _options.ignorePenumbra; int audioDataOffset = audioPacket->pos(); QElapsedTimer timer; timer.start(); int nextFrame = 0; bool shouldLoop = _options.loop; // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks quint16 outgoingInjectedAudioSequenceNumber = 0; while (_currentSendOffset < _audioData.size() && !_shouldStop) { int bytesToCopy = std::min(((_options.stereo) ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, _audioData.size() - _currentSendOffset); // 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() + _currentSendOffset + i)) / (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); } _loudness /= (float)(bytesToCopy / sizeof(int16_t)); audioPacket->seek(0); // pack the sequence number audioPacket->writePrimitive(outgoingInjectedAudioSequenceNumber); audioPacket->seek(positionOptionOffset); audioPacket->writePrimitive(_options.position); audioPacket->writePrimitive(_options.orientation); volume = MAX_INJECTOR_VOLUME * _options.volume; audioPacket->seek(volumeOptionOffset); audioPacket->writePrimitive(volume); audioPacket->seek(audioDataOffset); // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet audioPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy); // set the correct size used for this packet audioPacket->setPayloadSize(audioPacket->pos()); // grab our audio mixer from the NodeList, if it exists SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); if (audioMixer) { // send off this audio packet nodeList->sendUnreliablePacket(*audioPacket, *audioMixer); outgoingInjectedAudioSequenceNumber++; } _currentSendOffset += bytesToCopy; // send two packets before the first sleep so the mixer can start playback right away if (_currentSendOffset != bytesToCopy && _currentSendOffset < _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 && _currentSendOffset >= _audioData.size()) { _currentSendOffset = 0; } } } setIsFinished(true); _isPlaying = !_isFinished; // Which can be false if a restart was requested }
void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) { auto nodeList = DependencyManager::get<NodeList>(); // With EC keys, we receive a nonce from the metaverse server, which is signed // here with the private key and returned. Verification is done at server. bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest; int status; int certIDByteArraySize; int textByteArraySize; int challengingNodeUUIDByteArraySize; packet->readPrimitive(&certIDByteArraySize); packet->readPrimitive(&textByteArraySize); // returns a cast char*, size if (challengeOriginatedFromClient) { packet->readPrimitive(&challengingNodeUUIDByteArraySize); } // "encryptedText" is now a series of random bytes, a nonce QByteArray certID = packet->read(certIDByteArraySize); QByteArray text = packet->read(textByteArraySize); QByteArray challengingNodeUUID; if (challengeOriginatedFromClient) { challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize); } EC_KEY* ec = readKeys(keyFilePath().toStdString().c_str()); QString sig; if (ec) { ERR_clear_error(); sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with status = 1; } else { qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed."; status = -1; } EC_KEY_free(ec); QByteArray textByteArray; if (status > -1) { textByteArray = sig.toUtf8(); } textByteArraySize = textByteArray.size(); int certIDSize = certID.size(); // setup the packet if (challengeOriginatedFromClient) { auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, certIDSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), true); textPacket->writePrimitive(certIDSize); textPacket->writePrimitive(textByteArraySize); textPacket->writePrimitive(challengingNodeUUIDByteArraySize); textPacket->write(certID); textPacket->write(textByteArray); textPacket->write(challengingNodeUUID); qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID; nodeList->sendPacket(std::move(textPacket), *sendingNode); } else { auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + textByteArraySize + 2 * sizeof(int), true); textPacket->writePrimitive(certIDSize); textPacket->writePrimitive(textByteArraySize); textPacket->write(certID); textPacket->write(textByteArray); qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID; nodeList->sendPacket(std::move(textPacket), *sendingNode); } if (status == -1) { qCDebug(commerce) << "During entity ownership challenge, signing the text failed."; long error = ERR_get_error(); if (error != 0) { const char* error_str = ERR_error_string(error, NULL); qCWarning(entities) << "EC error:" << error_str; } } }
std::unique_ptr<NLPacket> createAudioPacket(PacketType type, int size, quint16 sequence, QString codec) { auto audioPacket = NLPacket::create(type, size); audioPacket->writePrimitive(sequence); audioPacket->writeString(codec); return audioPacket; }