Пример #1
0
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;
}
Пример #2
0
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);
            }
        });
    }
}
Пример #3
0
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);
    }
}
Пример #4
0
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);
    }
}
Пример #5
0
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);
    }
}
Пример #7
0
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.";
        }
    }
}
Пример #8
0
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);
    }
}
Пример #9
0
void
XspfDataWriter::writeTitle() {
	assert(this->d->data != NULL);
	XML_Char const * const title = this->d->data->getTitle();
	if (title != NULL) {
		writePrimitive(_PT("title"), title);
	}
}
Пример #10
0
void
XspfDataWriter::writeCreator() {
	assert(this->d->data != NULL);
	XML_Char const * const creator = this->d->data->getCreator();
	if (creator != NULL) {
		writePrimitive(_PT("creator"), creator);
	}
}
Пример #11
0
void
XspfDataWriter::writeAnnotation() {
	assert(this->d->data != NULL);
	XML_Char const * const annotation = this->d->data->getAnnotation();
	if (annotation != NULL) {
		writePrimitive(_PT("annotation"), annotation);
	}
}
Пример #12
0
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;
	}
}
Пример #13
0
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;
	}
}
Пример #14
0
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);
    });
}
Пример #15
0
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);
    }
}
Пример #16
0
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;
}
Пример #17
0
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);
    }
}
Пример #18
0
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();
}
Пример #19
0
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);
}
Пример #20
0
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);
    }
}
Пример #21
0
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);
    }
}
Пример #22
0
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;
}
Пример #23
0
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);
}
Пример #24
0
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);
    }
}
Пример #25
0
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();
}
Пример #26
0
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;
}
Пример #27
0
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);
}
Пример #28
0
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
}
Пример #29
0
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;
        }
    }
}
Пример #30
0
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;
}