void AssignmentClient::readPendingDatagrams() { NodeList* nodeList = NodeList::getInstance(); QByteArray receivedPacket; HifiSockAddr senderSockAddr; while (nodeList->getNodeSocket().hasPendingDatagrams()) { receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); if (nodeList->packetVersionAndHashMatch(receivedPacket)) { if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) { // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket); if (_currentAssignment) { qDebug() << "Received an assignment -" << *_currentAssignment; // switch our nodelist domain IP and port to whoever sent us the assignment nodeList->getDomainInfo().setSockAddr(senderSockAddr); nodeList->getDomainInfo().setAssignmentUUID(_currentAssignment->getUUID()); qDebug() << "Destination IP for assignment is" << nodeList->getDomainInfo().getIP().toString(); // start the deployed assignment QThread* workerThread = new QThread(this); connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); _currentAssignment->moveToThread(workerThread); // move the NodeList to the thread used for the _current assignment nodeList->moveToThread(workerThread); // let the assignment handle the incoming datagrams for its duration disconnect(&nodeList->getNodeSocket(), 0, this, 0); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _currentAssignment, &ThreadedAssignment::readPendingDatagrams); // Starts an event loop, and emits workerThread->started() workerThread->start(); } else { qDebug() << "Received an assignment that could not be unpacked. Re-requesting."; } } else { // have the NodeList attempt to handle it nodeList->processNodeData(senderSockAddr, receivedPacket); } } } }
void MetavoxelServer::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; NodeList* nodeList = NodeList::getInstance(); while (readAvailableDatagram(receivedPacket, senderSockAddr)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { switch (packetTypeForPacket(receivedPacket)) { case PacketTypeMetavoxelData: nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket); break; default: nodeList->processNodeData(senderSockAddr, receivedPacket); break; } } } }
void AudioMixer::readPendingDatagrams() { QByteArray receivedPacket; HifiSockAddr senderSockAddr; NodeList* nodeList = NodeList::getInstance(); while (readAvailableDatagram(receivedPacket, senderSockAddr)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) { // pull any new audio data from nodes off of the network stack PacketType mixerPacketType = packetTypeForPacket(receivedPacket); if (mixerPacketType == PacketTypeMicrophoneAudioNoEcho || mixerPacketType == PacketTypeMicrophoneAudioWithEcho || mixerPacketType == PacketTypeInjectAudio || mixerPacketType == PacketTypeSilentAudioFrame) { nodeList->findNodeAndUpdateWithDataFromPacket(receivedPacket); } else { // let processNodeData handle it. nodeList->processNodeData(senderSockAddr, receivedPacket); } } } }
int main(int argc, const char* argv[]) { NodeList* nodeList = NodeList::createInstance(NODE_TYPE_AVATAR_MIXER, AVATAR_LISTEN_PORT); setvbuf(stdout, NULL, _IOLBF, 0); // Handle Local Domain testing with the --local command line const char* local = "--local"; if (cmdOptionExists(argc, argv, local)) { printf("Local Domain MODE!\n"); nodeList->setDomainIPToLocalhost(); } nodeList->linkedDataCreateCallback = attachAvatarDataToNode; nodeList->startSilentNodeRemovalThread(); sockaddr* nodeAddress = new sockaddr; ssize_t receivedBytes = 0; unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE]; uint16_t nodeID = 0; Node* avatarNode = NULL; timeval lastDomainServerCheckIn = {}; // we only need to hear back about avatar nodes from the DS NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1); while (true) { // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); NodeList::getInstance()->sendDomainServerCheckIn(); } if (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) && packetVersionMatch(packetData)) { switch (packetData[0]) { case PACKET_TYPE_HEAD_DATA: // grab the node ID from the packet unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID); // add or update the node in our list avatarNode = nodeList->addOrUpdateNode(nodeAddress, nodeAddress, NODE_TYPE_AGENT, nodeID); // parse positional data from an node nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes); case PACKET_TYPE_INJECT_AUDIO: broadcastAvatarData(nodeList, nodeAddress); break; case PACKET_TYPE_AVATAR_VOXEL_URL: case PACKET_TYPE_AVATAR_FACE_VIDEO: // grab the node ID from the packet unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID); // let everyone else know about the update for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getActiveSocket() && node->getNodeID() != nodeID) { nodeList->getNodeSocket()->send(node->getActiveSocket(), packetData, receivedBytes); } } break; case PACKET_TYPE_DOMAIN: // ignore the DS packet, for now nodes are added only when they communicate directly with us break; default: // hand this off to the NodeList nodeList->processNodeData(nodeAddress, packetData, receivedBytes); break; } } } nodeList->stopSilentNodeRemovalThread(); return 0; }
void DatagramProcessor::processDatagrams() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "DatagramProcessor::processDatagrams()"); HifiSockAddr senderSockAddr; static QByteArray incomingPacket; Application* application = Application::getInstance(); NodeList* nodeList = NodeList::getInstance(); while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams()) { incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); _packetCount++; _byteCount += 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(&application->_audio, "parseAudioStreamStatsPacket", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else if (incomingType == PacketTypeAudioEnvironment) { QMetaObject::invokeMethod(&application->_audio, "parseAudioEnvironmentData", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else { QMetaObject::invokeMethod(&application->_audio, "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()); audioMixer->recordBytesReceived(incomingPacket.size()); } break; } case PacketTypeEntityAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly EntityItemID::handleAddEntityResponse(incomingPacket); application->getEntities()->getTree()->handleAddEntityResponse(incomingPacket); break; case PacketTypeEntityData: case PacketTypeEntityErase: case PacketTypeVoxelData: case PacketTypeVoxelErase: case PacketTypeOctreeStats: case PacketTypeEnvironmentData: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _octreeProcessor.queueReceivedPacket()"); bool wantExtraDebugging = application->getLogger()->extraDebugging(); if (wantExtraDebugging && packetTypeForPacket(incomingPacket) == PacketTypeVoxelData) { int numBytesPacketHeader = numBytesForPacketHeader(incomingPacket); unsigned char* dataAt = reinterpret_cast<unsigned char*>(incomingPacket.data()) + numBytesPacketHeader; dataAt += sizeof(OCTREE_PACKET_FLAGS); OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(OCTREE_PACKET_SEQUENCE); OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt); dataAt += sizeof(OCTREE_PACKET_SENT_TIME); OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int flightTime = arrivedAt - sentAt; qDebug("got an Octree data or erase message, sequence:%d flightTime:%d", sequence, flightTime); } SharedNodePointer matchedNode = NodeList::getInstance()->sendingNodeForPacket(incomingPacket); if (matchedNode) { // add this packet to our list of voxel packets and process them on the voxel processing application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket); } break; } case PacketTypeMetavoxelData: nodeList->findNodeAndUpdateWithDataFromPacket(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()); avatarMixer->recordBytesReceived(incomingPacket.size()); QMetaObject::invokeMethod(&application->getAvatarManager(), "processAvatarMixerDatagram", Q_ARG(const QByteArray&, incomingPacket), Q_ARG(const QWeakPointer<Node>&, avatarMixer)); } application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size()); break; } case PacketTypeDomainConnectionDenied: { // 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 qDebug() << "The domain-server denied a connection request."; qDebug() << "You may need to re-log to generate a keypair so you can provide a username signature."; AccountManager::getInstance().checkAndSignalForAccessToken(); break; } case PacketTypeNoisyMute: case PacketTypeMuteEnvironment: { bool mute = !Application::getInstance()->getAudio()->getMuted(); if (incomingType == PacketTypeMuteEnvironment) { glm::vec3 position; float radius, distance; int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment); memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3)); memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float)); distance = glm::distance(Application::getInstance()->getAvatar()->getPosition(), position); mute = mute && (distance < radius); } if (mute) { Application::getInstance()->getAudio()->toggleMute(); if (incomingType == PacketTypeMuteEnvironment) { AudioScriptingInterface::getInstance().environmentMuted(); } else { AudioScriptingInterface::getInstance().mutedByMixer(); } } break; } case PacketTypeVoxelEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_voxelEditSender.processNackPacket(incomingPacket); } break; case PacketTypeEntityEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_entityEditSender.processNackPacket(incomingPacket); } break; default: nodeList->processNodeData(senderSockAddr, incomingPacket); break; } } }
void AssignmentClient::readPendingDatagrams() { NodeList* nodeList = NodeList::getInstance(); static unsigned char packetData[1500]; static qint64 receivedBytes = 0; static HifiSockAddr senderSockAddr; while (nodeList->getNodeSocket().hasPendingDatagrams()) { if ((receivedBytes = nodeList->getNodeSocket().readDatagram((char*) packetData, MAX_PACKET_SIZE, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer())) && packetVersionMatch(packetData)) { if (_currentAssignment) { // have the threaded current assignment handle this datagram QMetaObject::invokeMethod(_currentAssignment, "processDatagram", Qt::QueuedConnection, Q_ARG(QByteArray, QByteArray((char*) packetData, receivedBytes)), Q_ARG(HifiSockAddr, senderSockAddr)); } else if (packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT || packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { if (_currentAssignment) { qDebug() << "Dropping received assignment since we are currently running one.\n"; } else { // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(packetData, receivedBytes); qDebug() << "Received an assignment -" << *_currentAssignment << "\n"; // switch our nodelist domain IP and port to whoever sent us the assignment if (packetData[0] == PACKET_TYPE_CREATE_ASSIGNMENT) { nodeList->setDomainSockAddr(senderSockAddr); nodeList->setOwnerUUID(_currentAssignment->getUUID()); qDebug("Destination IP for assignment is %s\n", nodeList->getDomainIP().toString().toStdString().c_str()); // start the deployed assignment QThread* workerThread = new QThread(this); connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); _currentAssignment->moveToThread(workerThread); // Starts an event loop, and emits workerThread->started() workerThread->start(); } else { qDebug("Received a bad destination socket for assignment.\n"); } } } else { // have the NodeList attempt to handle it nodeList->processNodeData(senderSockAddr, packetData, receivedBytes); } } } }
void DatagramProcessor::processDatagrams() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "DatagramProcessor::processDatagrams()"); HifiSockAddr senderSockAddr; static QByteArray incomingPacket; Application* application = Application::getInstance(); NodeList* nodeList = NodeList::getInstance(); while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams()) { incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); _packetCount++; _byteCount += incomingPacket.size(); if (nodeList->packetVersionAndHashMatch(incomingPacket)) { // only process this packet if we have a match on the packet version switch (packetTypeForPacket(incomingPacket)) { case PacketTypeTransmitterData: // V2 = IOS transmitter app application->getAvatar()->getTransmitter().processIncomingData(reinterpret_cast<unsigned char*>(incomingPacket.data()), incomingPacket.size()); break; case PacketTypeMixedAudio: QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToBuffer", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); break; case PacketTypeParticleAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse(incomingPacket); application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket); break; case PacketTypeParticleData: case PacketTypeParticleErase: case PacketTypeVoxelData: case PacketTypeVoxelErase: case PacketTypeOctreeStats: case PacketTypeEnvironmentData: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()"); bool wantExtraDebugging = application->getLogger()->extraDebugging(); if (wantExtraDebugging && packetTypeForPacket(incomingPacket) == PacketTypeVoxelData) { int numBytesPacketHeader = numBytesForPacketHeader(incomingPacket); unsigned char* dataAt = reinterpret_cast<unsigned char*>(incomingPacket.data()) + numBytesPacketHeader; dataAt += sizeof(OCTREE_PACKET_FLAGS); OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(OCTREE_PACKET_SEQUENCE); OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt); dataAt += sizeof(OCTREE_PACKET_SENT_TIME); OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int flightTime = arrivedAt - sentAt; printf("got PacketType_VOXEL_DATA, sequence:%d flightTime:%d\n", sequence, flightTime); } SharedNodePointer matchedNode = NodeList::getInstance()->sendingNodeForPacket(incomingPacket); if (matchedNode) { // add this packet to our list of voxel packets and process them on the voxel processing application->_voxelProcessor.queueReceivedPacket(matchedNode, incomingPacket); } break; } case PacketTypeMetavoxelData: nodeList->findNodeAndUpdateWithDataFromPacket(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()); avatarMixer->recordBytesReceived(incomingPacket.size()); QMetaObject::invokeMethod(&application->getAvatarManager(), "processAvatarMixerDatagram", Q_ARG(const QByteArray&, incomingPacket), Q_ARG(const QWeakPointer<Node>&, avatarMixer)); } application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size()); break; } case PacketTypeDataServerGet: case PacketTypeDataServerPut: case PacketTypeDataServerSend: case PacketTypeDataServerConfirm: DataServerClient::processMessageFromDataServer(incomingPacket); break; default: nodeList->processNodeData(senderSockAddr, incomingPacket); break; } } } }
void AssignmentClient::readPendingDatagrams() { NodeList* nodeList = NodeList::getInstance(); QByteArray receivedPacket; HifiSockAddr senderSockAddr; while (nodeList->getNodeSocket().hasPendingDatagrams()) { receivedPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(receivedPacket.data(), receivedPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); if (packetVersionMatch(receivedPacket)) { if (_currentAssignment) { // have the threaded current assignment handle this datagram QMetaObject::invokeMethod(_currentAssignment, "processDatagram", Qt::QueuedConnection, Q_ARG(QByteArray, receivedPacket), Q_ARG(HifiSockAddr, senderSockAddr)); } else if (packetTypeForPacket(receivedPacket) == PacketTypeCreateAssignment) { if (_currentAssignment) { qDebug() << "Dropping received assignment since we are currently running one."; } else { // construct the deployed assignment from the packet data _currentAssignment = AssignmentFactory::unpackAssignment(receivedPacket); if (_currentAssignment) { qDebug() << "Received an assignment -" << *_currentAssignment; // switch our nodelist domain IP and port to whoever sent us the assignment nodeList->setDomainSockAddr(senderSockAddr); nodeList->setOwnerUUID(_currentAssignment->getUUID()); qDebug() << "Destination IP for assignment is" << nodeList->getDomainIP().toString(); // start the deployed assignment QThread* workerThread = new QThread(this); connect(workerThread, SIGNAL(started()), _currentAssignment, SLOT(run())); connect(_currentAssignment, SIGNAL(finished()), this, SLOT(assignmentCompleted())); connect(_currentAssignment, SIGNAL(finished()), workerThread, SLOT(quit())); connect(_currentAssignment, SIGNAL(finished()), _currentAssignment, SLOT(deleteLater())); connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); _currentAssignment->moveToThread(workerThread); // move the NodeList to the thread used for the _current assignment nodeList->moveToThread(workerThread); // Starts an event loop, and emits workerThread->started() workerThread->start(); } else { qDebug() << "Received an assignment that could not be unpacked. Re-requesting."; } } } else { // have the NodeList attempt to handle it nodeList->processNodeData(senderSockAddr, receivedPacket); } } } }
int main(int argc, const char * argv[]) { pthread_mutex_init(&::treeLock, NULL); qInstallMessageHandler(sharedMessageHandler); NodeList* nodeList = NodeList::createInstance(NODE_TYPE_VOXEL_SERVER, VOXEL_LISTEN_PORT); setvbuf(stdout, NULL, _IOLBF, 0); // Handle Local Domain testing with the --local command line const char* local = "--local"; ::wantLocalDomain = cmdOptionExists(argc, argv,local); if (::wantLocalDomain) { printf("Local Domain MODE!\n"); nodeList->setDomainIPToLocalhost(); } nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode; nodeList->startSilentNodeRemovalThread(); srand((unsigned)time(0)); const char* DISPLAY_VOXEL_STATS = "--displayVoxelStats"; ::displayVoxelStats = cmdOptionExists(argc, argv, DISPLAY_VOXEL_STATS); printf("displayVoxelStats=%s\n", debug::valueOf(::displayVoxelStats)); const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending"; ::debugVoxelSending = cmdOptionExists(argc, argv, DEBUG_VOXEL_SENDING); printf("debugVoxelSending=%s\n", debug::valueOf(::debugVoxelSending)); const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving"; ::debugVoxelReceiving = cmdOptionExists(argc, argv, DEBUG_VOXEL_RECEIVING); printf("debugVoxelReceiving=%s\n", debug::valueOf(::debugVoxelReceiving)); const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug"; ::shouldShowAnimationDebug = cmdOptionExists(argc, argv, WANT_ANIMATION_DEBUG); printf("shouldShowAnimationDebug=%s\n", debug::valueOf(::shouldShowAnimationDebug)); const char* WANT_COLOR_RANDOMIZER = "--wantColorRandomizer"; ::wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER); printf("wantColorRandomizer=%s\n", debug::valueOf(::wantColorRandomizer)); // By default we will voxel persist, if you want to disable this, then pass in this parameter const char* NO_VOXEL_PERSIST = "--NoVoxelPersist"; if (cmdOptionExists(argc, argv, NO_VOXEL_PERSIST)) { ::wantVoxelPersist = false; } printf("wantVoxelPersist=%s\n", debug::valueOf(::wantVoxelPersist)); // if we want Voxel Persistance, load the local file now... bool persistantFileRead = false; if (::wantVoxelPersist) { printf("loading voxels from file...\n"); persistantFileRead = ::serverTree.readFromSVOFile(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE); if (persistantFileRead) { PerformanceWarning warn(::shouldShowAnimationDebug, "persistVoxelsWhenDirty() - reaverageVoxelColors()", ::shouldShowAnimationDebug); // after done inserting all these voxels, then reaverage colors serverTree.reaverageVoxelColors(serverTree.rootNode); printf("Voxels reAveraged\n"); } ::serverTree.clearDirtyBit(); // the tree is clean since we just loaded it printf("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); unsigned long nodeCount = ::serverTree.rootNode->getSubTreeNodeCount(); unsigned long internalNodeCount = ::serverTree.rootNode->getSubTreeInternalNodeCount(); unsigned long leafNodeCount = ::serverTree.rootNode->getSubTreeLeafNodeCount(); printf("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount); } // Check to see if the user passed in a command line option for loading an old style local // Voxel File. If so, load it now. This is not the same as a voxel persist file const char* INPUT_FILE = "-i"; const char* voxelsFilename = getCmdOption(argc, argv, INPUT_FILE); if (voxelsFilename) { serverTree.readFromSVOFile(voxelsFilename); } // Check to see if the user passed in a command line option for setting packet send rate const char* PACKETS_PER_SECOND = "--packetsPerSecond"; const char* packetsPerSecond = getCmdOption(argc, argv, PACKETS_PER_SECOND); if (packetsPerSecond) { PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/10; if (PACKETS_PER_CLIENT_PER_INTERVAL < 1) { PACKETS_PER_CLIENT_PER_INTERVAL = 1; } printf("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, PACKETS_PER_CLIENT_PER_INTERVAL); } const char* ADD_RANDOM_VOXELS = "--AddRandomVoxels"; if (cmdOptionExists(argc, argv, ADD_RANDOM_VOXELS)) { // create an octal code buffer and load it with 0 so that the recursive tree fill can give // octal codes to the tree nodes that it is creating randomlyFillVoxelTree(MAX_VOXEL_TREE_DEPTH_LEVELS, serverTree.rootNode); } const char* ADD_SCENE = "--AddScene"; bool addScene = cmdOptionExists(argc, argv, ADD_SCENE); const char* NO_ADD_SCENE = "--NoAddScene"; bool noAddScene = cmdOptionExists(argc, argv, NO_ADD_SCENE); if (addScene && noAddScene) { printf("WARNING! --AddScene and --NoAddScene are mutually exclusive. We will honor --NoAddScene\n"); } // We will add a scene if... // 1) we attempted to load a persistant file and it wasn't there // 2) you asked us to add a scene // HOWEVER -- we will NEVER add a scene if you explicitly tell us not to! // // TEMPORARILY DISABLED!!! bool actuallyAddScene = false; // !noAddScene && (addScene || (::wantVoxelPersist && !persistantFileRead)); if (actuallyAddScene) { addSphereScene(&serverTree); } // for now, initialize the environments with fixed values environmentData[1].setID(1); environmentData[1].setGravity(1.0f); environmentData[1].setAtmosphereCenter(glm::vec3(0.5, 0.5, (0.25 - 0.06125)) * (float)TREE_SCALE); environmentData[1].setAtmosphereInnerRadius(0.030625f * TREE_SCALE); environmentData[1].setAtmosphereOuterRadius(0.030625f * TREE_SCALE * 1.05f); environmentData[2].setID(2); environmentData[2].setGravity(1.0f); environmentData[2].setAtmosphereCenter(glm::vec3(0.5f, 0.5f, 0.5f) * (float)TREE_SCALE); environmentData[2].setAtmosphereInnerRadius(0.1875f * TREE_SCALE); environmentData[2].setAtmosphereOuterRadius(0.1875f * TREE_SCALE * 1.05f); environmentData[2].setScatteringWavelengths(glm::vec3(0.475f, 0.570f, 0.650f)); // swaps red and blue pthread_t sendVoxelThread; pthread_create(&sendVoxelThread, NULL, distributeVoxelsToListeners, NULL); sockaddr nodePublicAddress; unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE]; ssize_t receivedBytes; timeval lastDomainServerCheckIn = {}; // loop to send to nodes requesting data while (true) { // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); NodeList::getInstance()->sendDomainServerCheckIn(); } // check to see if we need to persist our voxel state persistVoxelsWhenDirty(); if (nodeList->getNodeSocket()->receive(&nodePublicAddress, packetData, &receivedBytes) && packetVersionMatch(packetData)) { int numBytesPacketHeader = numBytesForPacketHeader(packetData); if (packetData[0] == PACKET_TYPE_SET_VOXEL || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) { bool destructive = (packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE); PerformanceWarning warn(::shouldShowAnimationDebug, destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL", ::shouldShowAnimationDebug); ::receivedPacketCount++; unsigned short int itemNumber = (*((unsigned short int*)(packetData + numBytesPacketHeader))); if (::shouldShowAnimationDebug) { printf("got %s - command from client receivedBytes=%ld itemNumber=%d\n", destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL", receivedBytes,itemNumber); } if (::debugVoxelReceiving) { printf("got %s - %d command from client receivedBytes=%ld itemNumber=%d\n", destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL", ::receivedPacketCount, receivedBytes,itemNumber); } int atByte = numBytesPacketHeader + sizeof(itemNumber); unsigned char* voxelData = (unsigned char*)&packetData[atByte]; while (atByte < receivedBytes) { unsigned char octets = (unsigned char)*voxelData; const int COLOR_SIZE_IN_BYTES = 3; int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES; int voxelCodeSize = bytesRequiredForCodeLength(octets); // color randomization on insert int colorRandomizer = ::wantColorRandomizer ? randIntInRange (-50, 50) : 0; int red = voxelData[voxelCodeSize + 0]; int green = voxelData[voxelCodeSize + 1]; int blue = voxelData[voxelCodeSize + 2]; if (::shouldShowAnimationDebug) { printf("insert voxels - wantColorRandomizer=%s old r=%d,g=%d,b=%d \n", (::wantColorRandomizer?"yes":"no"),red,green,blue); } red = std::max(0, std::min(255, red + colorRandomizer)); green = std::max(0, std::min(255, green + colorRandomizer)); blue = std::max(0, std::min(255, blue + colorRandomizer)); if (::shouldShowAnimationDebug) { printf("insert voxels - wantColorRandomizer=%s NEW r=%d,g=%d,b=%d \n", (::wantColorRandomizer?"yes":"no"),red,green,blue); } voxelData[voxelCodeSize + 0] = red; voxelData[voxelCodeSize + 1] = green; voxelData[voxelCodeSize + 2] = blue; if (::shouldShowAnimationDebug) { float* vertices = firstVertexForCode(voxelData); printf("inserting voxel at: %f,%f,%f\n", vertices[0], vertices[1], vertices[2]); delete []vertices; } serverTree.readCodeColorBufferToTree(voxelData, destructive); // skip to next voxelData += voxelDataSize; atByte += voxelDataSize; } } else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) { // Send these bits off to the VoxelTree class to process them pthread_mutex_lock(&::treeLock); serverTree.processRemoveVoxelBitstream((unsigned char*)packetData, receivedBytes); pthread_mutex_unlock(&::treeLock); } else if (packetData[0] == PACKET_TYPE_Z_COMMAND) { // the Z command is a special command that allows the sender to send the voxel server high level semantic // requests, like erase all, or add sphere scene char* command = (char*) &packetData[numBytesPacketHeader]; // start of the command int commandLength = strlen(command); // commands are null terminated strings int totalLength = numBytesPacketHeader + commandLength + 1; // 1 for null termination printf("got Z message len(%ld)= %s\n", receivedBytes, command); bool rebroadcast = true; // by default rebroadcast while (totalLength <= receivedBytes) { if (strcmp(command, ERASE_ALL_COMMAND) == 0) { printf("got Z message == erase all\n"); eraseVoxelTreeAndCleanupNodeVisitData(); rebroadcast = false; } if (strcmp(command, ADD_SCENE_COMMAND) == 0) { printf("got Z message == add scene\n"); addSphereScene(&serverTree); rebroadcast = false; } if (strcmp(command, TEST_COMMAND) == 0) { printf("got Z message == a message, nothing to do, just report\n"); } totalLength += commandLength + 1; // 1 for null termination } if (rebroadcast) { // Now send this to the connected nodes so they can also process these messages printf("rebroadcasting Z message to connected nodes... nodeList.broadcastToNodes()\n"); nodeList->broadcastToNodes(packetData, receivedBytes, &NODE_TYPE_AGENT, 1); } } else if (packetData[0] == PACKET_TYPE_HEAD_DATA) { // If we got a PACKET_TYPE_HEAD_DATA, then we're talking to an NODE_TYPE_AVATAR, and we // need to make sure we have it in our nodeList. uint16_t nodeID = 0; unpackNodeId(packetData + numBytesPacketHeader, &nodeID); Node* node = nodeList->addOrUpdateNode(&nodePublicAddress, &nodePublicAddress, NODE_TYPE_AGENT, nodeID); nodeList->updateNodeWithData(node, packetData, receivedBytes); } else if (packetData[0] == PACKET_TYPE_PING) { // If the packet is a ping, let processNodeData handle it. nodeList->processNodeData(&nodePublicAddress, packetData, receivedBytes); } } } pthread_join(sendVoxelThread, NULL); pthread_mutex_destroy(&::treeLock); return 0; }
void AvatarMixer::run() { // change the logging target name while AvatarMixer is running Logging::setTargetName(AVATAR_MIXER_LOGGING_NAME); NodeList* nodeList = NodeList::getInstance(); nodeList->setOwnerType(NODE_TYPE_AVATAR_MIXER); nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1); nodeList->linkedDataCreateCallback = attachAvatarDataToNode; nodeList->startSilentNodeRemovalThread(); sockaddr nodeAddress = {}; ssize_t receivedBytes = 0; unsigned char packetData[MAX_PACKET_SIZE]; QUuid nodeUUID; Node* avatarNode = NULL; timeval lastDomainServerCheckIn = {}; while (true) { if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { break; } // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); NodeList::getInstance()->sendDomainServerCheckIn(); } nodeList->possiblyPingInactiveNodes(); if (nodeList->getNodeSocket()->receive(&nodeAddress, packetData, &receivedBytes) && packetVersionMatch(packetData)) { switch (packetData[0]) { case PACKET_TYPE_HEAD_DATA: nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData), NUM_BYTES_RFC4122_UUID)); // add or update the node in our list avatarNode = nodeList->nodeWithUUID(nodeUUID); if (avatarNode) { // parse positional data from an node nodeList->updateNodeWithData(avatarNode, &nodeAddress, packetData, receivedBytes); } else { break; } case PACKET_TYPE_INJECT_AUDIO: broadcastAvatarData(nodeList, nodeUUID, &nodeAddress); break; case PACKET_TYPE_AVATAR_URLS: case PACKET_TYPE_AVATAR_FACE_VIDEO: nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData + numBytesForPacketHeader(packetData), NUM_BYTES_RFC4122_UUID)); // let everyone else know about the update for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getActiveSocket() && node->getUUID() != nodeUUID) { nodeList->getNodeSocket()->send(node->getActiveSocket(), packetData, receivedBytes); } } break; default: // hand this off to the NodeList nodeList->processNodeData(&nodeAddress, packetData, receivedBytes); break; } } } nodeList->stopSilentNodeRemovalThread(); }
void DatagramProcessor::processDatagrams() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "DatagramProcessor::processDatagrams()"); HifiSockAddr senderSockAddr; static QByteArray incomingPacket; Application* application = Application::getInstance(); NodeList* nodeList = NodeList::getInstance(); while (NodeList::getInstance()->getNodeSocket().hasPendingDatagrams()) { incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize()); nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(), senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer()); _packetCount++; _byteCount += incomingPacket.size(); if (nodeList->packetVersionAndHashMatch(incomingPacket)) { // only process this packet if we have a match on the packet version switch (packetTypeForPacket(incomingPacket)) { case PacketTypeMixedAudio: case PacketTypeSilentAudioFrame: QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToStream", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); break; case PacketTypeAudioStreamStats: QMetaObject::invokeMethod(&application->_audio, "parseAudioStreamStatsPacket", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); break; case PacketTypeParticleAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse(incomingPacket); application->getParticles()->getTree()->handleAddParticleResponse(incomingPacket); break; case PacketTypeModelAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly ModelItem::handleAddModelResponse(incomingPacket); application->getModels()->getTree()->handleAddModelResponse(incomingPacket); break; case PacketTypeParticleData: case PacketTypeParticleErase: case PacketTypeModelData: case PacketTypeModelErase: case PacketTypeVoxelData: case PacketTypeVoxelErase: case PacketTypeOctreeStats: case PacketTypeEnvironmentData: { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::networkReceive()... _octreeProcessor.queueReceivedPacket()"); bool wantExtraDebugging = application->getLogger()->extraDebugging(); if (wantExtraDebugging && packetTypeForPacket(incomingPacket) == PacketTypeVoxelData) { int numBytesPacketHeader = numBytesForPacketHeader(incomingPacket); unsigned char* dataAt = reinterpret_cast<unsigned char*>(incomingPacket.data()) + numBytesPacketHeader; dataAt += sizeof(OCTREE_PACKET_FLAGS); OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt); dataAt += sizeof(OCTREE_PACKET_SEQUENCE); OCTREE_PACKET_SENT_TIME sentAt = (*(OCTREE_PACKET_SENT_TIME*)dataAt); dataAt += sizeof(OCTREE_PACKET_SENT_TIME); OCTREE_PACKET_SENT_TIME arrivedAt = usecTimestampNow(); int flightTime = arrivedAt - sentAt; qDebug("got PacketType_VOXEL_DATA, sequence:%d flightTime:%d", sequence, flightTime); } SharedNodePointer matchedNode = NodeList::getInstance()->sendingNodeForPacket(incomingPacket); if (matchedNode) { // add this packet to our list of voxel packets and process them on the voxel processing application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket); } break; } case PacketTypeMetavoxelData: nodeList->findNodeAndUpdateWithDataFromPacket(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()); avatarMixer->recordBytesReceived(incomingPacket.size()); QMetaObject::invokeMethod(&application->getAvatarManager(), "processAvatarMixerDatagram", Q_ARG(const QByteArray&, incomingPacket), Q_ARG(const QWeakPointer<Node>&, avatarMixer)); } application->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(incomingPacket.size()); break; } case PacketTypeDomainOAuthRequest: { QDataStream readStream(incomingPacket); readStream.skipRawData(numBytesForPacketHeader(incomingPacket)); QUrl authorizationURL; readStream >> authorizationURL; QMetaObject::invokeMethod(&OAuthWebViewHandler::getInstance(), "displayWebviewForAuthorizationURL", Q_ARG(const QUrl&, authorizationURL)); break; } case 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)); if (glm::distance(Application::getInstance()->getAvatar()->getPosition(), position) < radius && !Application::getInstance()->getAudio()->getMuted()) { Application::getInstance()->getAudio()->toggleMute(); } break; } case PacketTypeVoxelEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_voxelEditSender.processNackPacket(incomingPacket); } break; case PacketTypeParticleEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_particleEditSender.processNackPacket(incomingPacket); } break; case PacketTypeModelEditNack: if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableNackPackets)) { application->_modelEditSender.processNackPacket(incomingPacket); } break; default: nodeList->processNodeData(senderSockAddr, incomingPacket); break; } } } }
void AvatarMixer::run() { // change the logging target name while AvatarMixer is running Logging::setTargetName(AVATAR_MIXER_LOGGING_NAME); NodeList* nodeList = NodeList::getInstance(); nodeList->setOwnerType(NODE_TYPE_AVATAR_MIXER); nodeList->linkedDataCreateCallback = attachAvatarDataToNode; nodeList->startSilentNodeRemovalThread(); sockaddr* nodeAddress = new sockaddr; ssize_t receivedBytes = 0; unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE]; uint16_t nodeID = 0; Node* avatarNode = NULL; timeval lastDomainServerCheckIn = {}; // we only need to hear back about avatar nodes from the DS nodeList->setNodeTypesOfInterest(&NODE_TYPE_AGENT, 1); while (true) { if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { break; } // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); NodeList::getInstance()->sendDomainServerCheckIn(); } if (nodeList->getNodeSocket()->receive(nodeAddress, packetData, &receivedBytes) && packetVersionMatch(packetData)) { switch (packetData[0]) { case PACKET_TYPE_HEAD_DATA: // grab the node ID from the packet unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID); // add or update the node in our list avatarNode = nodeList->addOrUpdateNode(nodeAddress, nodeAddress, NODE_TYPE_AGENT, nodeID); // parse positional data from an node nodeList->updateNodeWithData(avatarNode, packetData, receivedBytes); case PACKET_TYPE_INJECT_AUDIO: broadcastAvatarData(nodeList, nodeAddress); break; case PACKET_TYPE_AVATAR_VOXEL_URL: case PACKET_TYPE_AVATAR_FACE_VIDEO: // grab the node ID from the packet unpackNodeId(packetData + numBytesForPacketHeader(packetData), &nodeID); // let everyone else know about the update for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { if (node->getActiveSocket() && node->getNodeID() != nodeID) { nodeList->getNodeSocket()->send(node->getActiveSocket(), packetData, receivedBytes); } } break; default: // hand this off to the NodeList nodeList->processNodeData(nodeAddress, packetData, receivedBytes); break; } } } nodeList->stopSilentNodeRemovalThread(); }