removed not yet ready for prime time libraries

This commit is contained in:
ZappoMan 2013-12-04 12:09:05 -08:00
parent 2b27c91550
commit bc5317f20b
14 changed files with 0 additions and 2424 deletions

View file

@ -1,42 +0,0 @@
cmake_minimum_required(VERSION 2.8)
set(ROOT_DIR ../..)
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
# setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
set(TARGET_NAME particle-server)
find_package(Qt5Widgets REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
# grab cJSON and civetweb sources to pass as OPTIONAL_SRCS
FILE(GLOB OPTIONAL_SRCS ${ROOT_DIR}/externals/civetweb/src/*)
setup_hifi_library(${TARGET_NAME} ${OPTIONAL_SRCS})
include_directories(${ROOT_DIR}/externals/civetweb/include)
qt5_use_modules(${TARGET_NAME} Widgets)
# inluce GLM
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
# link in the shared library
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link ZLIB
find_package(ZLIB)
include_directories(${ZLIB_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
# link in the hifi octree library
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi particles library
link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})

View file

@ -1,86 +0,0 @@
//
// ParticlePersistThread.cpp
// particle-server
//
// Created by Brad Hefta-Gaub on 12/2/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded particle persistence
//
#include <QDebug>
#include <NodeList.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include "ParticlePersistThread.h"
#include "ParticleServer.h"
ParticlePersistThread::ParticlePersistThread(ParticleTree* tree, const char* filename, int persistInterval) :
_tree(tree),
_filename(filename),
_persistInterval(persistInterval),
_initialLoadComplete(false),
_loadTimeUSecs(0) {
}
bool ParticlePersistThread::process() {
if (!_initialLoadComplete) {
uint64_t loadStarted = usecTimestampNow();
qDebug("loading particles from file: %s...\n", _filename);
bool persistantFileRead;
_tree->lockForWrite();
{
PerformanceWarning warn(true, "Loading Particle File", true);
persistantFileRead = _tree->readFromSVOFile(_filename);
}
_tree->unlock();
_loadCompleted = time(0);
uint64_t loadDone = usecTimestampNow();
_loadTimeUSecs = loadDone - loadStarted;
_tree->clearDirtyBit(); // the tree is clean since we just loaded it
qDebug("DONE loading particles from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
unsigned long nodeCount = ParticleNode::getNodeCount();
unsigned long internalNodeCount = ParticleNode::getInternalNodeCount();
unsigned long leafNodeCount = ParticleNode::getLeafNodeCount();
qDebug("Nodes after loading scene %lu nodes %lu internal %lu leaves\n", nodeCount, internalNodeCount, leafNodeCount);
double usecPerGet = (double)ParticleNode::getGetChildAtIndexTime() / (double)ParticleNode::getGetChildAtIndexCalls();
qDebug("getChildAtIndexCalls=%llu getChildAtIndexTime=%llu perGet=%lf \n",
ParticleNode::getGetChildAtIndexTime(), ParticleNode::getGetChildAtIndexCalls(), usecPerGet);
double usecPerSet = (double)ParticleNode::getSetChildAtIndexTime() / (double)ParticleNode::getSetChildAtIndexCalls();
qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n",
ParticleNode::getSetChildAtIndexTime(), ParticleNode::getSetChildAtIndexCalls(), usecPerSet);
_initialLoadComplete = true;
_lastCheck = usecTimestampNow(); // we just loaded, no need to save again
}
if (isStillRunning()) {
uint64_t MSECS_TO_USECS = 1000;
uint64_t USECS_TO_SLEEP = 100 * MSECS_TO_USECS; // every 100ms
usleep(USECS_TO_SLEEP);
uint64_t now = usecTimestampNow();
uint64_t sinceLastSave = now - _lastCheck;
uint64_t intervalToCheck = _persistInterval * MSECS_TO_USECS;
if (sinceLastSave > intervalToCheck) {
// check the dirty bit and persist here...
_lastCheck = usecTimestampNow();
if (_tree->isDirty()) {
qDebug("saving particles to file %s...\n",_filename);
_tree->writeToSVOFile(_filename);
_tree->clearDirtyBit(); // tree is clean after saving
qDebug("DONE saving particles to file...\n");
}
}
}
return isStillRunning(); // keep running till they terminate us
}

View file

@ -1,44 +0,0 @@
//
// ParticlePersistThread.h
// particle-server
//
// Created by Brad Hefta-Gaub on 12/2/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded particle persistence
//
#ifndef __particle_server__ParticlePersistThread__
#define __particle_server__ParticlePersistThread__
#include <GenericThread.h>
#include <NetworkPacket.h>
#include <ParticleTree.h>
/// Generalized threaded processor for handling received inbound packets.
class ParticlePersistThread : public virtual GenericThread {
public:
static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
ParticlePersistThread(ParticleTree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL);
bool isInitialLoadComplete() const { return _initialLoadComplete; }
time_t* getLoadCompleted() { return &_loadCompleted; }
uint64_t getLoadElapsedTime() const { return _loadTimeUSecs; }
protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
private:
ParticleTree* _tree;
const char* _filename;
int _persistInterval;
bool _initialLoadComplete;
time_t _loadCompleted;
uint64_t _loadTimeUSecs;
uint64_t _lastCheck;
};
#endif // __particle_server__ParticlePersistThread__

View file

@ -1,276 +0,0 @@
//
// ParticleReceiverData.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 12/2/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "PacketHeaders.h"
#include "SharedUtil.h"
#include "ParticleReceiverData.h"
#include <cstring>
#include <cstdio>
#include "ParticleSendThread.h"
ParticleReceiverData::ParticleReceiverData(Node* owningNode) :
ParticleQuery(owningNode),
_viewSent(false),
_particlePacketAvailableBytes(MAX_PACKET_SIZE),
_maxSearchLevel(1),
_maxLevelReachedInLastSearch(1),
_lastTimeBagEmpty(0),
_viewFrustumChanging(false),
_viewFrustumJustStoppedChanging(true),
_currentPacketIsColor(true),
_currentPacketIsCompressed(false),
_particleSendThread(NULL),
_lastClientBoundaryLevelAdjust(0),
_lastClientParticleSizeScale(DEFAULT_PARTICLE_SIZE_SCALE),
_lodChanged(false),
_lodInitialized(false)
{
_particlePacket = new unsigned char[MAX_PACKET_SIZE];
_particlePacketAt = _particlePacket;
_lastParticlePacket = new unsigned char[MAX_PACKET_SIZE];
_lastParticlePacketLength = 0;
_duplicatePacketCount = 0;
_sequenceNumber = 0;
resetParticlePacket(true); // don't bump sequence
}
void ParticleReceiverData::initializeParticleSendThread(ParticleServer* particleServer) {
// Create particle sending thread...
QUuid nodeUUID = getOwningNode()->getUUID();
_particleSendThread = new ParticleSendThread(nodeUUID, particleServer);
_particleSendThread->initialize(true);
}
bool ParticleReceiverData::packetIsDuplicate() const {
// since our packets now include header information, like sequence number, and createTime, we can't just do a memcmp
// of the entire packet, we need to compare only the packet content...
if (_lastParticlePacketLength == getPacketLength()) {
return memcmp(_lastParticlePacket + PARTICLE_PACKET_HEADER_SIZE,
_particlePacket+PARTICLE_PACKET_HEADER_SIZE , getPacketLength() - PARTICLE_PACKET_HEADER_SIZE) == 0;
}
return false;
}
bool ParticleReceiverData::shouldSuppressDuplicatePacket() {
bool shouldSuppress = false; // assume we won't suppress
// only consider duplicate packets
if (packetIsDuplicate()) {
_duplicatePacketCount++;
// If this is the first suppressed packet, remember our time...
if (_duplicatePacketCount == 1) {
_firstSuppressedPacket = usecTimestampNow();
}
// How long has it been since we've sent one, if we're still under our max time, then keep considering
// this packet for suppression
uint64_t now = usecTimestampNow();
long sinceFirstSuppressedPacket = now - _firstSuppressedPacket;
const long MAX_TIME_BETWEEN_DUPLICATE_PACKETS = 1000 * 1000; // 1 second.
if (sinceFirstSuppressedPacket < MAX_TIME_BETWEEN_DUPLICATE_PACKETS) {
// Finally, if we know we've sent at least one duplicate out, then suppress the rest...
if (_duplicatePacketCount >= 1) {
shouldSuppress = true;
}
} else {
// Reset our count, we've reached our maximum time.
_duplicatePacketCount = 0;
}
} else {
// Reset our count, it wasn't a duplicate
_duplicatePacketCount = 0;
}
return shouldSuppress;
}
void ParticleReceiverData::resetParticlePacket(bool lastWasSurpressed) {
// Whenever we call this, we will keep a copy of the last packet, so we can determine if the last packet has
// changed since we last reset it. Since we know that no two packets can ever be identical without being the same
// scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing
// packet send rate.
_lastParticlePacketLength = getPacketLength();
memcpy(_lastParticlePacket, _particlePacket, _lastParticlePacketLength);
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state.
_currentPacketIsColor = getWantColor();
_currentPacketIsCompressed = getWantCompression();
PARTICLE_PACKET_FLAGS flags = 0;
if (_currentPacketIsColor) {
setAtBit(flags,PACKET_IS_COLOR_BIT);
}
if (_currentPacketIsCompressed) {
setAtBit(flags,PACKET_IS_COMPRESSED_BIT);
}
_particlePacketAvailableBytes = MAX_PACKET_SIZE;
int numBytesPacketHeader = populateTypeAndVersion(_particlePacket, PACKET_TYPE_PARTICLE_DATA);
_particlePacketAt = _particlePacket + numBytesPacketHeader;
_particlePacketAvailableBytes -= numBytesPacketHeader;
// pack in flags
PARTICLE_PACKET_FLAGS* flagsAt = (PARTICLE_PACKET_FLAGS*)_particlePacketAt;
*flagsAt = flags;
_particlePacketAt += sizeof(PARTICLE_PACKET_FLAGS);
_particlePacketAvailableBytes -= sizeof(PARTICLE_PACKET_FLAGS);
// pack in sequence number
PARTICLE_PACKET_SEQUENCE* sequenceAt = (PARTICLE_PACKET_SEQUENCE*)_particlePacketAt;
*sequenceAt = _sequenceNumber;
_particlePacketAt += sizeof(PARTICLE_PACKET_SEQUENCE);
_particlePacketAvailableBytes -= sizeof(PARTICLE_PACKET_SEQUENCE);
if (!(lastWasSurpressed || _lastParticlePacketLength == PARTICLE_PACKET_HEADER_SIZE)) {
_sequenceNumber++;
}
// pack in timestamp
PARTICLE_PACKET_SENT_TIME now = usecTimestampNow();
PARTICLE_PACKET_SENT_TIME* timeAt = (PARTICLE_PACKET_SENT_TIME*)_particlePacketAt;
*timeAt = now;
_particlePacketAt += sizeof(PARTICLE_PACKET_SENT_TIME);
_particlePacketAvailableBytes -= sizeof(PARTICLE_PACKET_SENT_TIME);
_particlePacketWaiting = false;
}
void ParticleReceiverData::writeToPacket(const unsigned char* buffer, int bytes) {
// compressed packets include lead bytes which contain compressed size, this allows packing of
// multiple compressed portions together
if (_currentPacketIsCompressed) {
*(PARTICLE_PACKET_INTERNAL_SECTION_SIZE*)_particlePacketAt = bytes;
_particlePacketAt += sizeof(PARTICLE_PACKET_INTERNAL_SECTION_SIZE);
_particlePacketAvailableBytes -= sizeof(PARTICLE_PACKET_INTERNAL_SECTION_SIZE);
}
if (bytes <= _particlePacketAvailableBytes) {
memcpy(_particlePacketAt, buffer, bytes);
_particlePacketAvailableBytes -= bytes;
_particlePacketAt += bytes;
_particlePacketWaiting = true;
}
}
ParticleReceiverData::~ParticleReceiverData() {
delete[] _particlePacket;
delete[] _lastParticlePacket;
if (_particleSendThread) {
_particleSendThread->terminate();
delete _particleSendThread;
}
}
bool ParticleReceiverData::updateCurrentViewFrustum() {
bool currentViewFrustumChanged = false;
ViewFrustum newestViewFrustum;
// get position and orientation details from the camera
newestViewFrustum.setPosition(getCameraPosition());
newestViewFrustum.setOrientation(getCameraOrientation());
// Also make sure it's got the correct lens details from the camera
float originalFOV = getCameraFov();
float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
newestViewFrustum.setFieldOfView(wideFOV); // hack
newestViewFrustum.setAspectRatio(getCameraAspectRatio());
newestViewFrustum.setNearClip(getCameraNearClip());
newestViewFrustum.setFarClip(getCameraFarClip());
newestViewFrustum.setEyeOffsetPosition(getCameraEyeOffsetPosition());
// if there has been a change, then recalculate
if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) {
_currentViewFrustum = newestViewFrustum;
_currentViewFrustum.calculate();
currentViewFrustumChanged = true;
}
// Also check for LOD changes from the client
if (_lodInitialized) {
if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) {
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
_lodChanged = true;
}
if (_lastClientParticleSizeScale != getParticleSizeScale()) {
_lastClientParticleSizeScale = getParticleSizeScale();
_lodChanged = true;
}
} else {
_lodInitialized = true;
_lastClientParticleSizeScale = getParticleSizeScale();
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
_lodChanged = false;
}
// When we first detect that the view stopped changing, we record this.
// but we don't change it back to false until we've completely sent this
// scene.
if (_viewFrustumChanging && !currentViewFrustumChanged) {
_viewFrustumJustStoppedChanging = true;
}
_viewFrustumChanging = currentViewFrustumChanged;
return currentViewFrustumChanged;
}
void ParticleReceiverData::setViewSent(bool viewSent) {
_viewSent = viewSent;
if (viewSent) {
_viewFrustumJustStoppedChanging = false;
_lodChanged = false;
}
}
void ParticleReceiverData::updateLastKnownViewFrustum() {
bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum);
if (frustumChanges) {
// save our currentViewFrustum into our lastKnownViewFrustum
_lastKnownViewFrustum = _currentViewFrustum;
}
// save that we know the view has been sent.
uint64_t now = usecTimestampNow();
setLastTimeBagEmpty(now); // is this what we want? poor names
}
bool ParticleReceiverData::moveShouldDump() const {
glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition();
glm::vec3 newPosition = _currentViewFrustum.getPosition();
// theoretically we could make this slightly larger but relative to avatar scale.
const float MAXIMUM_MOVE_WITHOUT_DUMP = 0.0f;
if (glm::distance(newPosition, oldPosition) > MAXIMUM_MOVE_WITHOUT_DUMP) {
return true;
}
return false;
}
void ParticleReceiverData::dumpOutOfView() {
int stillInView = 0;
int outOfView = 0;
ParticleNodeBag tempBag;
while (!nodeBag.isEmpty()) {
ParticleNode* node = nodeBag.extract();
if (node->isInView(_currentViewFrustum)) {
tempBag.insert(node);
stillInView++;
} else {
outOfView++;
}
}
if (stillInView > 0) {
while (!tempBag.isEmpty()) {
ParticleNode* node = tempBag.extract();
if (node->isInView(_currentViewFrustum)) {
nodeBag.insert(node);
}
}
}
}

View file

@ -1,123 +0,0 @@
//
// ParticleReceiverData.h
// hifi
//
// Created by Brad Hefta-Gaub on 12/2/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __hifi__ParticleReceiverData__
#define __hifi__ParticleReceiverData__
#include <iostream>
#include <NodeData.h>
#include <ParticlePacketData.h>
#include <ParticleQuery.h>
#include <CoverageMap.h>
#include <ParticleConstants.h>
#include <ParticleNodeBag.h>
#include <ParticleSceneStats.h>
class ParticleSendThread;
class ParticleServer;
class ParticleReceiverData : public ParticleQuery {
public:
ParticleReceiverData(Node* owningNode);
virtual ~ParticleReceiverData();
void resetParticlePacket(bool lastWasSurpressed = false); // resets particle packet to after "V" header
void writeToPacket(const unsigned char* buffer, int bytes); // writes to end of packet
const unsigned char* getPacket() const { return _particlePacket; }
int getPacketLength() const { return (MAX_PACKET_SIZE - _particlePacketAvailableBytes); }
bool isPacketWaiting() const { return _particlePacketWaiting; }
bool packetIsDuplicate() const;
bool shouldSuppressDuplicatePacket();
int getAvailable() const { return _particlePacketAvailableBytes; }
int getMaxSearchLevel() const { return _maxSearchLevel; }
void resetMaxSearchLevel() { _maxSearchLevel = 1; }
void incrementMaxSearchLevel() { _maxSearchLevel++; }
int getMaxLevelReached() const { return _maxLevelReachedInLastSearch; }
void setMaxLevelReached(int maxLevelReached) { _maxLevelReachedInLastSearch = maxLevelReached; }
ParticleNodeBag nodeBag;
CoverageMap map;
ViewFrustum& getCurrentViewFrustum() { return _currentViewFrustum; }
ViewFrustum& getLastKnownViewFrustum() { return _lastKnownViewFrustum; }
// These are not classic setters because they are calculating and maintaining state
// which is set asynchronously through the network receive
bool updateCurrentViewFrustum();
void updateLastKnownViewFrustum();
bool getViewSent() const { return _viewSent; }
void setViewSent(bool viewSent);
bool getViewFrustumChanging() const { return _viewFrustumChanging; }
bool getViewFrustumJustStoppedChanging() const { return _viewFrustumJustStoppedChanging; }
bool moveShouldDump() const;
uint64_t getLastTimeBagEmpty() const { return _lastTimeBagEmpty; }
void setLastTimeBagEmpty(uint64_t lastTimeBagEmpty) { _lastTimeBagEmpty = lastTimeBagEmpty; }
bool getCurrentPacketIsColor() const { return _currentPacketIsColor; }
bool getCurrentPacketIsCompressed() const { return _currentPacketIsCompressed; }
bool getCurrentPacketFormatMatches() {
return (getCurrentPacketIsColor() == getWantColor() && getCurrentPacketIsCompressed() == getWantCompression());
}
bool hasLodChanged() const { return _lodChanged; };
ParticleSceneStats stats;
void initializeParticleSendThread(ParticleServer* particleServer);
bool isParticleSendThreadInitalized() { return _particleSendThread; }
void dumpOutOfView();
private:
ParticleReceiverData(const ParticleReceiverData &);
ParticleReceiverData& operator= (const ParticleReceiverData&);
bool _viewSent;
unsigned char* _particlePacket;
unsigned char* _particlePacketAt;
int _particlePacketAvailableBytes;
bool _particlePacketWaiting;
unsigned char* _lastParticlePacket;
int _lastParticlePacketLength;
int _duplicatePacketCount;
uint64_t _firstSuppressedPacket;
int _maxSearchLevel;
int _maxLevelReachedInLastSearch;
ViewFrustum _currentViewFrustum;
ViewFrustum _lastKnownViewFrustum;
uint64_t _lastTimeBagEmpty;
bool _viewFrustumChanging;
bool _viewFrustumJustStoppedChanging;
bool _currentPacketIsColor;
bool _currentPacketIsCompressed;
ParticleSendThread* _particleSendThread;
// watch for LOD changes
int _lastClientBoundaryLevelAdjust;
float _lastClientParticleSizeScale;
bool _lodChanged;
bool _lodInitialized;
PARTICLE_PACKET_SEQUENCE _sequenceNumber;
};
#endif /* defined(__hifi__ParticleReceiverData__) */

View file

@ -1,551 +0,0 @@
//
// ParticleSendThread.cpp
// particle-server
//
// Created by Brad Hefta-Gaub on 12/2/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded particle packet sender
//
#include <NodeList.h>
#include <PacketHeaders.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include "ParticleSendThread.h"
#include "ParticleServer.h"
#include "ParticleServerConsts.h"
uint64_t startSceneSleepTime = 0;
uint64_t endSceneSleepTime = 0;
ParticleSendThread::ParticleSendThread(const QUuid& nodeUUID, ParticleServer* myServer) :
_nodeUUID(nodeUUID),
_myServer(myServer),
_packetData()
{
}
bool ParticleSendThread::process() {
uint64_t start = usecTimestampNow();
bool gotLock = false;
// don't do any send processing until the initial load of the particles is complete...
if (_myServer->isInitialLoadComplete()) {
Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID);
if (node) {
// make sure the node list doesn't kill our node while we're using it
if (node->trylock()) {
gotLock = true;
ParticleReceiverData* nodeData = NULL;
nodeData = (ParticleReceiverData*) node->getLinkedData();
int packetsSent = 0;
// Sometimes the node data has not yet been linked, in which case we can't really do anything
if (nodeData) {
bool viewFrustumChanged = nodeData->updateCurrentViewFrustum();
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
packetsSent = deepestLevelParticleDistributor(node, nodeData, viewFrustumChanged);
}
node->unlock(); // we're done with this node for now.
}
}
} else {
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
qDebug("ParticleSendThread::process() waiting for isInitialLoadComplete()\n");
}
}
// Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap
if (isStillRunning() && gotLock) {
// dynamically sleep until we need to fire off the next set of particles
int elapsed = (usecTimestampNow() - start);
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed;
if (usecToSleep > 0) {
PerformanceWarning warn(false,"ParticleSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
usleep(usecToSleep);
} else {
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
std::cout << "Last send took too much time, not sleeping!\n";
}
}
}
return isStillRunning(); // keep running till they terminate us
}
uint64_t ParticleSendThread::_usleepTime = 0;
uint64_t ParticleSendThread::_usleepCalls = 0;
uint64_t ParticleSendThread::_totalBytes = 0;
uint64_t ParticleSendThread::_totalWastedBytes = 0;
uint64_t ParticleSendThread::_totalPackets = 0;
int ParticleSendThread::handlePacketSend(Node* node, ParticleReceiverData* nodeData, int& trueBytesSent, int& truePacketsSent) {
bool debug = _myServer->wantsDebugParticleSending();
uint64_t now = usecTimestampNow();
bool packetSent = false; // did we send a packet?
int packetsSent = 0;
// Here's where we check to see if this packet is a duplicate of the last packet. If it is, we will silently
// obscure the packet and not send it. This allows the callers and upper level logic to not need to know about
// this rate control savings.
if (nodeData->shouldSuppressDuplicatePacket()) {
nodeData->resetParticlePacket(true); // we still need to reset it though!
return packetsSent; // without sending...
}
const unsigned char* messageData = nodeData->getPacket();
int numBytesPacketHeader = numBytesForPacketHeader(messageData);
const unsigned char* dataAt = messageData + numBytesPacketHeader;
dataAt += sizeof(VOXEL_PACKET_FLAGS);
VOXEL_PACKET_SEQUENCE sequence = (*(VOXEL_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(VOXEL_PACKET_SEQUENCE);
// If we've got a stats message ready to send, then see if we can piggyback them together
if (nodeData->stats.isReadyToSend()) {
// Send the stats message to the client
unsigned char* statsMessage = nodeData->stats.getStatsMessage();
int statsMessageLength = nodeData->stats.getStatsMessageLength();
// If the size of the stats message and the particle message will fit in a packet, then piggyback them
if (nodeData->getPacketLength() + statsMessageLength < MAX_PACKET_SIZE) {
// copy particle message to back of stats message
memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength());
statsMessageLength += nodeData->getPacketLength();
// since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
// there was nothing else to send.
int thisWastedBytes = 0;
_totalWastedBytes += thisWastedBytes;
_totalBytes += nodeData->getPacketLength();
_totalPackets++;
if (debug) {
qDebug("Adding stats to packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n",
now,
_totalPackets,
sequence, nodeData->getPacketLength(), _totalBytes,
thisWastedBytes, _totalWastedBytes);
}
// actually send it
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
packetSent = true;
} else {
// not enough room in the packet, send two packets
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
// since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since
// there was nothing else to send.
int thisWastedBytes = 0;
_totalWastedBytes += thisWastedBytes;
_totalBytes += statsMessageLength;
_totalPackets++;
if (debug) {
qDebug("Sending separate stats packet at %llu [%llu]: size:%d [%llu] wasted bytes:%d [%llu]\n",
now,
_totalPackets,
statsMessageLength, _totalBytes,
thisWastedBytes, _totalWastedBytes);
}
trueBytesSent += statsMessageLength;
truePacketsSent++;
packetsSent++;
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
nodeData->getPacket(), nodeData->getPacketLength());
packetSent = true;
thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
_totalWastedBytes += thisWastedBytes;
_totalBytes += nodeData->getPacketLength();
_totalPackets++;
if (debug) {
qDebug("Sending packet at %llu [%llu]: sequence: %d size:%d [%llu] wasted bytes:%d [%llu]\n",
now,
_totalPackets,
sequence, nodeData->getPacketLength(), _totalBytes,
thisWastedBytes, _totalWastedBytes);
}
}
nodeData->stats.markAsSent();
} else {
// If there's actually a packet waiting, then send it.
if (nodeData->isPacketWaiting()) {
// just send the particle packet
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
nodeData->getPacket(), nodeData->getPacketLength());
packetSent = true;
int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
_totalWastedBytes += thisWastedBytes;
_totalBytes += nodeData->getPacketLength();
_totalPackets++;
if (debug) {
qDebug("Sending packet at %llu [%llu]: sequence:%d size:%d [%llu] wasted bytes:%d [%llu]\n",
now,
_totalPackets,
sequence, nodeData->getPacketLength(), _totalBytes,
thisWastedBytes, _totalWastedBytes);
}
}
}
// remember to track our stats
if (packetSent) {
nodeData->stats.packetSent(nodeData->getPacketLength());
trueBytesSent += nodeData->getPacketLength();
truePacketsSent++;
packetsSent++;
nodeData->resetParticlePacket();
}
return packetsSent;
}
/// Version of particle distributor that sends the deepest LOD level at once
int ParticleSendThread::deepestLevelParticleDistributor(Node* node, ParticleReceiverData* nodeData, bool viewFrustumChanged) {
int truePacketsSent = 0;
int trueBytesSent = 0;
int packetsSentThisInterval = 0;
bool somethingToSend = true; // assume we have something
// FOR NOW... node tells us if it wants to receive only view frustum deltas
bool wantDelta = viewFrustumChanged && nodeData->getWantDelta();
// If our packet already has content in it, then we must use the color choice of the waiting packet.
// If we're starting a fresh packet, then...
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state.
bool wantColor = nodeData->getWantColor();
bool wantCompression = nodeData->getWantCompression();
// If we have a packet waiting, and our desired want color, doesn't match the current waiting packets color
// then let's just send that waiting packet.
if (!nodeData->getCurrentPacketFormatMatches()) {
if (nodeData->isPacketWaiting()) {
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s currentPacketIsCompressed=%s\n",
debug::valueOf(wantColor), debug::valueOf(wantCompression),
debug::valueOf(nodeData->getCurrentPacketIsColor()),
debug::valueOf(nodeData->getCurrentPacketIsCompressed()) );
}
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
} else {
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s\n",
debug::valueOf(wantColor), debug::valueOf(wantCompression),
debug::valueOf(nodeData->getCurrentPacketIsColor()),
debug::valueOf(nodeData->getCurrentPacketIsCompressed()) );
}
nodeData->resetParticlePacket();
}
int targetSize = MAX_VOXEL_PACKET_DATA_SIZE;
if (wantCompression) {
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
}
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__,
debug::valueOf(wantCompression), targetSize);
}
_packetData.changeSettings(wantCompression, targetSize);
}
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s\n",
debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()),
debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()),
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving()));
}
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("deepestLevelParticleDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()),
debug::valueOf(nodeData->getViewSent())
);
}
// If the current view frustum has changed OR we have nothing to send, then search against
// the current view frustum for things to send.
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
uint64_t now = usecTimestampNow();
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
if (nodeData->getLastTimeBagEmpty() > 0) {
float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f;
if (viewFrustumChanged) {
printf("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend);
} else {
printf("elapsed time to send scene = %f seconds", elapsedSceneSend);
}
printf(" [occlusionCulling:%s, wantDelta:%s, wantColor:%s ]\n",
debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta),
debug::valueOf(wantColor));
}
}
// if our view has changed, we need to reset these things...
if (viewFrustumChanged) {
if (_myServer->wantDumpParticlesOnMove() || nodeData->moveShouldDump() || nodeData->hasLodChanged()) {
nodeData->dumpOutOfView();
}
nodeData->map.erase();
}
if (!viewFrustumChanged && !nodeData->getWantDelta()) {
// only set our last sent time if we weren't resetting due to frustum change
uint64_t now = usecTimestampNow();
nodeData->setLastTimeBagEmpty(now);
}
// track completed scenes and send out the stats packet accordingly
nodeData->stats.sceneCompleted();
::endSceneSleepTime = _usleepTime;
unsigned long sleepTime = ::endSceneSleepTime - ::startSceneSleepTime;
unsigned long encodeTime = nodeData->stats.getTotalEncodeTime();
unsigned long elapsedTime = nodeData->stats.getElapsedTime();
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
if (_myServer->wantsDebugParticleSending()) {
qDebug("Scene completed at %llu encodeTime:%lu sleepTime:%lu elapsed:%lu Packets:%llu Bytes:%llu Wasted:%llu\n",
usecTimestampNow(), encodeTime, sleepTime, elapsedTime, _totalPackets, _totalBytes, _totalWastedBytes);
}
if (_myServer->wantDisplayParticleStats()) {
nodeData->stats.printDebugDetails();
}
// start tracking our stats
bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta())
&& nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged();
// If we're starting a full scene, then definitely we want to empty the nodeBag
if (isFullScene) {
nodeData->nodeBag.deleteAll();
}
if (_myServer->wantsDebugParticleSending()) {
qDebug("Scene started at %llu Packets:%llu Bytes:%llu Wasted:%llu\n",
usecTimestampNow(),_totalPackets,_totalBytes,_totalWastedBytes);
}
::startSceneSleepTime = _usleepTime;
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged,
_myServer->getServerTree().rootNode, _myServer->getJurisdiction());
// This is the start of "resending" the scene.
bool dontRestartSceneOnMove = false; // this is experimental
if (dontRestartSceneOnMove) {
if (nodeData->nodeBag.isEmpty()) {
nodeData->nodeBag.insert(_myServer->getServerTree().rootNode); // only in case of empty
}
} else {
nodeData->nodeBag.insert(_myServer->getServerTree().rootNode); // original behavior, reset on move or empty
}
}
// If we have something in our nodeBag, then turn them into packets and send them out...
if (!nodeData->nodeBag.isEmpty()) {
int bytesWritten = 0;
uint64_t start = usecTimestampNow();
uint64_t startCompressTimeMsecs = ParticlePacketData::getCompressContentTime() / 1000;
uint64_t startCompressCalls = ParticlePacketData::getCompressContentCalls();
int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxParticlePacketsPerSecond() / INTERVALS_PER_SECOND));
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxParticlePacketsPerSecond(), clientMaxPacketsPerInterval);
}
int extraPackingAttempts = 0;
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) {
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxParticlePacketsPerSecond(), clientMaxPacketsPerInterval);
}
bool lastNodeDidntFit = false; // assume each node fits
if (!nodeData->nodeBag.isEmpty()) {
ParticleNode* subTree = nodeData->nodeBag.extract();
bool wantOcclusionCulling = nodeData->getWantOcclusionCulling();
CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP;
float particleSizeScale = nodeData->getParticleSizeScale();
int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust();
int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving()
? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta()) &&
nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged();
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum,
wantOcclusionCulling, coverageMap, boundaryLevelAdjust, particleSizeScale,
nodeData->getLastTimeBagEmpty(),
isFullScene, &nodeData->stats, _myServer->getJurisdiction());
_myServer->getServerTree().lockForRead();
nodeData->stats.encodeStarted();
bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params);
// if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case.
if (_packetData.getTargetSize() == MAX_VOXEL_PACKET_DATA_SIZE) {
if (_packetData.hasContent() && bytesWritten == 0 &&
params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
lastNodeDidntFit = true;
}
} else {
// in compressed mode and we are trying to pack more... and we don't care if the _packetData has
// content or not... because in this case even if we were unable to pack any data, we want to drop
// below to our sendNow logic, but we do want to track that we attempted to pack extra
extraPackingAttempts++;
if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
lastNodeDidntFit = true;
}
}
nodeData->stats.encodeStopped();
_myServer->getServerTree().unlock();
} else {
// If the bag was empty then we didn't even attempt to encode, and so we know the bytesWritten were 0
bytesWritten = 0;
somethingToSend = false; // this will cause us to drop out of the loop...
}
// If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a
// little bit more in this packet. To do this we
// We only consider sending anything if there is something in the _packetData to send... But
// if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases
// mean we should send the previous packet contents and reset it.
if (lastNodeDidntFit) {
if (_packetData.hasContent()) {
// if for some reason the finalized size is greater than our available size, then probably the "compressed"
// form actually inflated beyond our padding, and in this case we will send the current packet, then
// write to out new packet...
int writtenSize = _packetData.getFinalizedSize()
+ (nodeData->getCurrentPacketIsCompressed() ? sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) : 0);
if (writtenSize > nodeData->getAvailable()) {
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("writtenSize[%d] > available[%d] too big, sending packet as is.\n",
writtenSize, nodeData->getAvailable());
}
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
}
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n",
nodeData->getAvailable(), _packetData.getFinalizedSize(),
_packetData.getUncompressedSize(), _packetData.getTargetSize());
}
nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize());
extraPackingAttempts = 0;
}
// If we're not running compressed, the we know we can just send now. Or if we're running compressed, but
// the packet doesn't have enough space to bother attempting to pack more...
bool sendNow = true;
if (nodeData->getCurrentPacketIsCompressed() &&
nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING &&
extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) {
sendNow = false; // try to pack more
}
int targetSize = MAX_VOXEL_PACKET_DATA_SIZE;
if (sendNow) {
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
if (wantCompression) {
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
}
} else {
// If we're in compressed mode, then we want to see if we have room for more in this wire packet.
// but we've finalized the _packetData, so we want to start a new section, we will do that by
// resetting the packet settings with the max uncompressed size of our current available space
// in the wire packet. We also include room for our section header, and a little bit of padding
// to account for the fact that whenc compressing small amounts of data, we sometimes end up with
// a larger compressed size then uncompressed size
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING;
}
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__,
debug::valueOf(nodeData->getWantCompression()), targetSize);
}
_packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset
}
}
uint64_t end = usecTimestampNow();
int elapsedmsec = (end - start)/1000;
uint64_t endCompressCalls = ParticlePacketData::getCompressContentCalls();
int elapsedCompressCalls = endCompressCalls - startCompressCalls;
uint64_t endCompressTimeMsecs = ParticlePacketData::getCompressContentTime() / 1000;
int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
int elapsedsec = (end - start)/1000000;
printf("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets %d nodes still to send\n",
elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
} else {
printf("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
}
} else if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, trueBytesSent, truePacketsSent, nodeData->nodeBag.count());
}
// if after sending packets we've emptied our bag, then we want to remember that we've sent all
// the particles from the current view frustum
if (nodeData->nodeBag.isEmpty()) {
nodeData->updateLastKnownViewFrustum();
nodeData->setViewSent(true);
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
nodeData->map.printStats();
}
nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes
}
if (_myServer->wantsDebugParticleSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxParticlePacketsPerSecond(), clientMaxPacketsPerInterval);
}
} // end if bag wasn't empty, and so we sent stuff...
return truePacketsSent;
}

View file

@ -1,47 +0,0 @@
//
// ParticleSendThread.h
// particle-server
//
// Created by Brad Hefta-Gaub on 12/2/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded object for sending particles to a client
//
#ifndef __particle_server__ParticleSendThread__
#define __particle_server__ParticleSendThread__
#include <GenericThread.h>
#include <NetworkPacket.h>
#include <ParticleTree.h>
#include <ParticleNodeBag.h>
#include "ParticleReceiverData.h"
#include "ParticleServer.h"
/// Threaded processor for sending particle packets to a single client
class ParticleSendThread : public virtual GenericThread {
public:
ParticleSendThread(const QUuid& nodeUUID, ParticleServer* myServer);
static uint64_t _totalBytes;
static uint64_t _totalWastedBytes;
static uint64_t _totalPackets;
static uint64_t _usleepTime;
static uint64_t _usleepCalls;
protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
private:
QUuid _nodeUUID;
ParticleServer* _myServer;
int handlePacketSend(Node* node, ParticleReceiverData* nodeData, int& trueBytesSent, int& truePacketsSent);
int deepestLevelParticleDistributor(Node* node, ParticleReceiverData* nodeData, bool viewFrustumChanged);
ParticlePacketData _packetData;
};
#endif // __particle_server__ParticleSendThread__

View file

@ -1,802 +0,0 @@
//
// ParticleSever.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 12/2/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <time.h>
#include <QtCore/QDebug>
#include <QtCore/QString>
#include <QtCore/QUuid>
#include <Logging.h>
#include <OctalCode.h>
#include <NodeList.h>
#include <NodeTypes.h>
//#include <ParticleTree.h>
#include "ParticleReceiverData.h"
#include <SharedUtil.h>
#include <PacketHeaders.h>
#include <SceneUtils.h>
#include <PerfStat.h>
#include <JurisdictionSender.h>
#include <UUID.h>
#ifdef _WIN32
#include "Syssocket.h"
#include "Systime.h"
#else
#include <sys/time.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#endif
#include "ParticleSever.h"
#include "ParticleSeverConsts.h"
const char* LOCAL_PARTICLES_PERSIST_FILE = "resources/particles.svo";
const char* PARTICLES_PERSIST_FILE = "/etc/highfidelity/particle-server/resources/particles.svo";
void attachParticleReceiverDataToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
ParticleReceiverData* particleNodeData = new ParticleReceiverData(newNode);
newNode->setLinkedData(particleNodeData);
}
}
ParticleSever* ParticleSever::_theInstance = NULL;
ParticleSever::ParticleSever(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuffer, numBytes),
_serverTree(true) {
_argc = 0;
_argv = NULL;
_packetsPerClientPerInterval = 10;
_wantParticlePersist = true;
_wantLocalDomain = false;
_debugParticleSending = false;
_shouldShowAnimationDebug = false;
_displayParticleStats = false;
_debugParticleReceiving = false;
_sendEnvironments = true;
_sendMinimalEnvironment = false;
_dumpParticlesOnMove = false;
_verboseDebug = false;
_jurisdiction = NULL;
_jurisdictionSender = NULL;
_ParticleSeverPacketProcessor = NULL;
_particlePersistThread = NULL;
_parsedArgV = NULL;
_started = time(0);
_startedUSecs = usecTimestampNow();
_theInstance = this;
}
ParticleSever::~ParticleSever() {
if (_parsedArgV) {
for (int i = 0; i < _argc; i++) {
delete[] _parsedArgV[i];
}
delete[] _parsedArgV;
}
}
void ParticleSever::initMongoose(int port) {
// setup the mongoose web server
struct mg_callbacks callbacks = {};
QString documentRoot = QString("%1/resources/web").arg(QCoreApplication::applicationDirPath());
QString listenPort = QString("%1").arg(port);
// list of options. Last element must be NULL.
const char* options[] = {
"listening_ports", listenPort.toLocal8Bit().constData(),
"document_root", documentRoot.toLocal8Bit().constData(),
NULL };
callbacks.begin_request = civetwebRequestHandler;
// Start the web server.
mg_start(&callbacks, NULL, options);
}
int ParticleSever::civetwebRequestHandler(struct mg_connection* connection) {
const struct mg_request_info* ri = mg_get_request_info(connection);
ParticleSever* theServer = GetInstance();
#ifdef FORCE_CRASH
if (strcmp(ri->uri, "/force_crash") == 0 && strcmp(ri->request_method, "GET") == 0) {
qDebug() << "About to force a crash!\n";
int foo;
int* forceCrash = &foo;
mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n\r\n");
mg_printf(connection, "%s", "forcing a crash....\r\n");
delete[] forceCrash;
mg_printf(connection, "%s", "did it crash....\r\n");
return 1;
}
#endif
bool showStats = false;
if (strcmp(ri->uri, "/") == 0 && strcmp(ri->request_method, "GET") == 0) {
showStats = true;
}
if (strcmp(ri->uri, "/resetStats") == 0 && strcmp(ri->request_method, "GET") == 0) {
theServer->_ParticleSeverPacketProcessor->resetStats();
showStats = true;
}
if (showStats) {
uint64_t checkSum;
// return a 200
mg_printf(connection, "%s", "HTTP/1.0 200 OK\r\n");
mg_printf(connection, "%s", "Content-Type: text/html\r\n\r\n");
mg_printf(connection, "%s", "<html><doc>\r\n");
mg_printf(connection, "%s", "<pre>\r\n");
mg_printf(connection, "%s", "<b>Your Particle Server is running... <a href='/'>[RELOAD]</a></b>\r\n");
tm* localtm = localtime(&theServer->_started);
const int MAX_TIME_LENGTH = 128;
char buffer[MAX_TIME_LENGTH];
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
mg_printf(connection, "Running since: %s", buffer);
// Convert now to tm struct for UTC
tm* gmtm = gmtime(&theServer->_started);
if (gmtm != NULL) {
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", gmtm);
mg_printf(connection, " [%s UTM] ", buffer);
}
mg_printf(connection, "%s", "\r\n");
uint64_t now = usecTimestampNow();
const int USECS_PER_MSEC = 1000;
uint64_t msecsElapsed = (now - theServer->_startedUSecs) / USECS_PER_MSEC;
const int MSECS_PER_SEC = 1000;
const int SECS_PER_MIN = 60;
const int MIN_PER_HOUR = 60;
const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN;
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
mg_printf(connection, "%s", "Uptime: ");
if (hours > 0) {
mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
}
if (minutes > 0) {
mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
}
if (seconds > 0) {
mg_printf(connection, "%.3f seconds ", seconds);
}
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "\r\n");
// display particle file load time
if (theServer->isInitialLoadComplete()) {
time_t* loadCompleted = theServer->getLoadCompleted();
if (loadCompleted) {
tm* particlesLoadedAtLocal = localtime(loadCompleted);
const int MAX_TIME_LENGTH = 128;
char buffer[MAX_TIME_LENGTH];
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", particlesLoadedAtLocal);
mg_printf(connection, "Particles Loaded At: %s", buffer);
// Convert now to tm struct for UTC
tm* particlesLoadedAtUTM = gmtime(theServer->getLoadCompleted());
if (gmtm != NULL) {
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", particlesLoadedAtUTM);
mg_printf(connection, " [%s UTM] ", buffer);
}
} else {
mg_printf(connection, "%s", "Particle Persist Disabled...\r\n");
}
mg_printf(connection, "%s", "\r\n");
uint64_t msecsElapsed = theServer->getLoadElapsedTime() / USECS_PER_MSEC;;
float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC;
int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR;
int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR));
mg_printf(connection, "%s", "Particles Load Took: ");
if (hours > 0) {
mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
}
if (minutes > 0) {
mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : "");
}
if (seconds >= 0) {
mg_printf(connection, "%.3f seconds", seconds);
}
mg_printf(connection, "%s", "\r\n");
} else {
mg_printf(connection, "%s", "Particles not yet loaded...\r\n");
}
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "<b>Configuration:</b>\r\n");
for (int i = 1; i < theServer->_argc; i++) {
mg_printf(connection, "%s ", theServer->_argv[i]);
}
mg_printf(connection, "%s", "\r\n"); // one to end the config line
mg_printf(connection, "%s", "\r\n"); // two more for spacing
mg_printf(connection, "%s", "\r\n");
// display scene stats
unsigned long nodeCount = ParticleNode::getNodeCount();
unsigned long internalNodeCount = ParticleNode::getInternalNodeCount();
unsigned long leafNodeCount = ParticleNode::getLeafNodeCount();
QLocale locale(QLocale::English);
const float AS_PERCENT = 100.0;
mg_printf(connection, "%s", "<b>Current Nodes in scene:</b>\r\n");
mg_printf(connection, " Total Nodes: %s nodes\r\n",
locale.toString((uint)nodeCount).rightJustified(16, ' ').toLocal8Bit().constData());
mg_printf(connection, " Internal Nodes: %s nodes (%5.2f%%)\r\n",
locale.toString((uint)internalNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
((float)internalNodeCount / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Leaf Nodes: %s nodes (%5.2f%%)\r\n",
locale.toString((uint)leafNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(),
((float)leafNodeCount / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "\r\n");
// display outbound packet stats
mg_printf(connection, "%s", "<b>Particle Packet Statistics...</b>\r\n");
uint64_t totalOutboundPackets = ParticleSendThread::_totalPackets;
uint64_t totalOutboundBytes = ParticleSendThread::_totalBytes;
uint64_t totalWastedBytes = ParticleSendThread::_totalWastedBytes;
uint64_t totalBytesOfOctalCodes = ParticlePacketData::getTotalBytesOfOctalCodes();
uint64_t totalBytesOfBitMasks = ParticlePacketData::getTotalBytesOfBitMasks();
uint64_t totalBytesOfColor = ParticlePacketData::getTotalBytesOfColor();
const int COLUMN_WIDTH = 10;
mg_printf(connection, " Total Outbound Packets: %s packets\r\n",
locale.toString((uint)totalOutboundPackets).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Total Outbound Bytes: %s bytes\r\n",
locale.toString((uint)totalOutboundBytes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Total Wasted Bytes: %s bytes\r\n",
locale.toString((uint)totalWastedBytes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Total OctalCode Bytes: %s bytes (%5.2f%%)\r\n",
locale.toString((uint)totalBytesOfOctalCodes).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
((float)totalBytesOfOctalCodes / (float)totalOutboundBytes) * AS_PERCENT);
mg_printf(connection, " Total BitMasks Bytes: %s bytes (%5.2f%%)\r\n",
locale.toString((uint)totalBytesOfBitMasks).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
((float)totalBytesOfBitMasks / (float)totalOutboundBytes) * AS_PERCENT);
mg_printf(connection, " Total Color Bytes: %s bytes (%5.2f%%)\r\n",
locale.toString((uint)totalBytesOfColor).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData(),
((float)totalBytesOfColor / (float)totalOutboundBytes) * AS_PERCENT);
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "\r\n");
// display inbound packet stats
mg_printf(connection, "%s", "<b>Particle Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n");
uint64_t averageTransitTimePerPacket = theServer->_ParticleSeverPacketProcessor->getAverageTransitTimePerPacket();
uint64_t averageProcessTimePerPacket = theServer->_particleServerPacketProcessor->getAverageProcessTimePerPacket();
uint64_t averageLockWaitTimePerPacket = theServer->_particleServerPacketProcessor->getAverageLockWaitTimePerPacket();
uint64_t averageProcessTimePerParticle = theServer->_particleServerPacketProcessor->getAverageProcessTimePerParticle();
uint64_t averageLockWaitTimePerParticle = theServer->_particleServerPacketProcessor->getAverageLockWaitTimePerParticle();
uint64_t totalParticlesProcessed = theServer->_particleServerPacketProcessor->getTotalParticlesProcessed();
uint64_t totalPacketsProcessed = theServer->_particleServerPacketProcessor->getTotalPacketsProcessed();
float averageParticlesPerPacket = totalPacketsProcessed == 0 ? 0 : totalParticlesProcessed / totalPacketsProcessed;
mg_printf(connection, " Total Inbound Packets: %s packets\r\n",
locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Total Inbound Particles: %s particles\r\n",
locale.toString((uint)totalParticlesProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Inbound Particles/Packet: %f particles/packet\r\n", averageParticlesPerPacket);
mg_printf(connection, " Average Transit Time/Packet: %s usecs\r\n",
locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Packet: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Packet: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Particle: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerParticle).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Particle: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerParticle).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
int senderNumber = 0;
NodeToSenderStatsMap& allSenderStats = theServer->_particleServerPacketProcessor->getSingleSenderStats();
for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
senderNumber++;
QUuid senderID = i->first;
SingleSenderStats& senderStats = i->second;
mg_printf(connection, "\r\n Stats for sender %d uuid: %s\r\n", senderNumber,
senderID.toString().toLocal8Bit().constData());
averageTransitTimePerPacket = senderStats.getAverageTransitTimePerPacket();
averageProcessTimePerPacket = senderStats.getAverageProcessTimePerPacket();
averageLockWaitTimePerPacket = senderStats.getAverageLockWaitTimePerPacket();
averageProcessTimePerParticle = senderStats.getAverageProcessTimePerParticle();
averageLockWaitTimePerParticle = senderStats.getAverageLockWaitTimePerParticle();
totalParticlesProcessed = senderStats.getTotalParticlesProcessed();
totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
averageParticlesPerPacket = totalPacketsProcessed == 0 ? 0 : totalParticlesProcessed / totalPacketsProcessed;
mg_printf(connection, " Total Inbound Packets: %s packets\r\n",
locale.toString((uint)totalPacketsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Total Inbound Particles: %s particles\r\n",
locale.toString((uint)totalParticlesProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Inbound Particles/Packet: %f particles/packet\r\n", averageParticlesPerPacket);
mg_printf(connection, " Average Transit Time/Packet: %s usecs\r\n",
locale.toString((uint)averageTransitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Packet: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Packet: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerPacket).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Particle: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerParticle).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Particle: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerParticle).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
}
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "\r\n");
// display memory usage stats
mg_printf(connection, "%s", "<b>Current Memory Usage Statistics</b>\r\n");
mg_printf(connection, "\r\nParticleNode size... %ld bytes\r\n", sizeof(ParticleNode));
mg_printf(connection, "%s", "\r\n");
const char* memoryScaleLabel;
const float MEGABYTES = 1000000.f;
const float GIGABYTES = 1000000000.f;
float memoryScale;
if (ParticleNode::getTotalMemoryUsage() / MEGABYTES < 1000.0f) {
memoryScaleLabel = "MB";
memoryScale = MEGABYTES;
} else {
memoryScaleLabel = "GB";
memoryScale = GIGABYTES;
}
mg_printf(connection, "Particle Node Memory Usage: %8.2f %s\r\n",
ParticleNode::getParticleMemoryUsage() / memoryScale, memoryScaleLabel);
mg_printf(connection, "Octcode Memory Usage: %8.2f %s\r\n",
ParticleNode::getOctcodeMemoryUsage() / memoryScale, memoryScaleLabel);
mg_printf(connection, "External Children Memory Usage: %8.2f %s\r\n",
ParticleNode::getExternalChildrenMemoryUsage() / memoryScale, memoryScaleLabel);
mg_printf(connection, "%s", " -----------\r\n");
mg_printf(connection, " Total: %8.2f %s\r\n",
ParticleNode::getTotalMemoryUsage() / memoryScale, memoryScaleLabel);
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "ParticleNode Children Population Statistics...\r\n");
checkSum = 0;
for (int i=0; i <= NUMBER_OF_CHILDREN; i++) {
checkSum += ParticleNode::getChildrenCount(i);
mg_printf(connection, " Nodes with %d children: %s nodes (%5.2f%%)\r\n", i,
locale.toString((uint)ParticleNode::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(),
((float)ParticleNode::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT);
}
mg_printf(connection, "%s", " ----------------------\r\n");
mg_printf(connection, " Total: %s nodes\r\n",
locale.toString((uint)checkSum).rightJustified(16, ' ').toLocal8Bit().constData());
#ifdef BLENDED_UNION_CHILDREN
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "ParticleNode Children Encoding Statistics...\r\n");
mg_printf(connection, " Single or No Children: %10.llu nodes (%5.2f%%)\r\n",
ParticleNode::getSingleChildrenCount(), ((float)ParticleNode::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
ParticleNode::getTwoChildrenOffsetCount(),
((float)ParticleNode::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Two Children as External: %10.llu nodes (%5.2f%%)\r\n",
ParticleNode::getTwoChildrenExternalCount(),
((float)ParticleNode::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n",
ParticleNode::getThreeChildrenOffsetCount(),
((float)ParticleNode::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Three Children as External: %10.llu nodes (%5.2f%%)\r\n",
ParticleNode::getThreeChildrenExternalCount(),
((float)ParticleNode::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT);
mg_printf(connection, " Children as External Array: %10.llu nodes (%5.2f%%)\r\n",
ParticleNode::getExternalChildrenCount(),
((float)ParticleNode::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT);
checkSum = ParticleNode::getSingleChildrenCount() +
ParticleNode::getTwoChildrenOffsetCount() + ParticleNode::getTwoChildrenExternalCount() +
ParticleNode::getThreeChildrenOffsetCount() + ParticleNode::getThreeChildrenExternalCount() +
ParticleNode::getExternalChildrenCount();
mg_printf(connection, "%s", " ----------------\r\n");
mg_printf(connection, " Total: %10.llu nodes\r\n", checkSum);
mg_printf(connection, " Expected: %10.lu nodes\r\n", nodeCount);
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "In other news....\r\n");
mg_printf(connection, "could store 4 children internally: %10.llu nodes\r\n",
ParticleNode::getCouldStoreFourChildrenInternally());
mg_printf(connection, "could NOT store 4 children internally: %10.llu nodes\r\n",
ParticleNode::getCouldNotStoreFourChildrenInternally());
#endif
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "</pre>\r\n");
mg_printf(connection, "%s", "</doc></html>");
return 1;
} else {
// have mongoose process this request from the document_root
return 0;
}
}
void ParticleSever::setArguments(int argc, char** argv) {
_argc = argc;
_argv = const_cast<const char**>(argv);
qDebug("ParticleSever::setArguments()\n");
for (int i = 0; i < _argc; i++) {
qDebug("_argv[%d]=%s\n", i, _argv[i]);
}
}
void ParticleSever::parsePayload() {
if (getNumPayloadBytes() > 0) {
QString config((const char*) _payload);
// Now, parse the config
QStringList configList = config.split(" ");
int argCount = configList.size() + 1;
qDebug("ParticleSever::parsePayload()... argCount=%d\n",argCount);
_parsedArgV = new char*[argCount];
const char* dummy = "config-from-payload";
_parsedArgV[0] = new char[strlen(dummy) + sizeof(char)];
strcpy(_parsedArgV[0], dummy);
for (int i = 1; i < argCount; i++) {
QString configItem = configList.at(i-1);
_parsedArgV[i] = new char[configItem.length() + sizeof(char)];
strcpy(_parsedArgV[i], configItem.toLocal8Bit().constData());
qDebug("ParticleSever::parsePayload()... _parsedArgV[%d]=%s\n", i, _parsedArgV[i]);
}
setArguments(argCount, _parsedArgV);
}
}
//int main(int argc, const char * argv[]) {
void ParticleSever::run() {
const char PARTICLE_SERVER_LOGGING_TARGET_NAME[] = "particle-server";
// change the logging target name while this is running
Logging::setTargetName(PARTICLE_SERVER_LOGGING_TARGET_NAME);
// Now would be a good time to parse our arguments, if we got them as assignment
if (getNumPayloadBytes() > 0) {
parsePayload();
}
qInstallMessageHandler(Logging::verboseMessageHandler);
const char* STATUS_PORT = "--statusPort";
const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT);
if (statusPort) {
int statusPortNumber = atoi(statusPort);
initMongoose(statusPortNumber);
}
const char* JURISDICTION_FILE = "--jurisdictionFile";
const char* jurisdictionFile = getCmdOption(_argc, _argv, JURISDICTION_FILE);
if (jurisdictionFile) {
qDebug("jurisdictionFile=%s\n", jurisdictionFile);
qDebug("about to readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
_jurisdiction = new JurisdictionMap(jurisdictionFile);
qDebug("after readFromFile().... jurisdictionFile=%s\n", jurisdictionFile);
} else {
const char* JURISDICTION_ROOT = "--jurisdictionRoot";
const char* jurisdictionRoot = getCmdOption(_argc, _argv, JURISDICTION_ROOT);
if (jurisdictionRoot) {
qDebug("jurisdictionRoot=%s\n", jurisdictionRoot);
}
const char* JURISDICTION_ENDNODES = "--jurisdictionEndNodes";
const char* jurisdictionEndNodes = getCmdOption(_argc, _argv, JURISDICTION_ENDNODES);
if (jurisdictionEndNodes) {
qDebug("jurisdictionEndNodes=%s\n", jurisdictionEndNodes);
}
if (jurisdictionRoot || jurisdictionEndNodes) {
_jurisdiction = new JurisdictionMap(jurisdictionRoot, jurisdictionEndNodes);
}
}
// should we send environments? Default is yes, but this command line suppresses sending
const char* DUMP_PARTICLES_ON_MOVE = "--dumpParticlesOnMove";
_dumpParticlesOnMove = cmdOptionExists(_argc, _argv, DUMP_PARTICLES_ON_MOVE);
qDebug("dumpParticlesOnMove=%s\n", debug::valueOf(_dumpParticlesOnMove));
// should we send environments? Default is yes, but this command line suppresses sending
const char* SEND_ENVIRONMENTS = "--sendEnvironments";
bool dontSendEnvironments = !cmdOptionExists(_argc, _argv, SEND_ENVIRONMENTS);
if (dontSendEnvironments) {
qDebug("Sending environments suppressed...\n");
_sendEnvironments = false;
} else {
// should we send environments? Default is yes, but this command line suppresses sending
const char* MINIMAL_ENVIRONMENT = "--minimalEnvironment";
_sendMinimalEnvironment = cmdOptionExists(_argc, _argv, MINIMAL_ENVIRONMENT);
qDebug("Using Minimal Environment=%s\n", debug::valueOf(_sendMinimalEnvironment));
}
qDebug("Sending environments=%s\n", debug::valueOf(_sendEnvironments));
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_PARTICLE_SERVER);
// we need to ask the DS about agents so we can ping/reply with them
const char nodeTypesOfInterest[] = { NODE_TYPE_AGENT, NODE_TYPE_ANIMATION_SERVER};
nodeList->setNodeTypesOfInterest(nodeTypesOfInterest, sizeof(nodeTypesOfInterest));
setvbuf(stdout, NULL, _IOLBF, 0);
// tell our NodeList about our desire to get notifications
nodeList->addHook(&_nodeWatcher);
nodeList->linkedDataCreateCallback = &attachParticleReceiverDataToNode;
nodeList->startSilentNodeRemovalThread();
srand((unsigned)time(0));
const char* DISPLAY_PARTICLE_STATS = "--displayParticleStats";
_displayParticleStats = cmdOptionExists(_argc, _argv, DISPLAY_PARTICLE_STATS);
qDebug("displayParticleStats=%s\n", debug::valueOf(_displayParticleStats));
const char* VERBOSE_DEBUG = "--verboseDebug";
_verboseDebug = cmdOptionExists(_argc, _argv, VERBOSE_DEBUG);
qDebug("verboseDebug=%s\n", debug::valueOf(_verboseDebug));
const char* DEBUG_PARTICLE_SENDING = "--debugParticleSending";
_debugParticleSending = cmdOptionExists(_argc, _argv, DEBUG_PARTICLE_SENDING);
qDebug("debugParticleSending=%s\n", debug::valueOf(_debugParticleSending));
const char* DEBUG_PARTICLE_RECEIVING = "--debugParticleReceiving";
_debugParticleReceiving = cmdOptionExists(_argc, _argv, DEBUG_PARTICLE_RECEIVING);
qDebug("debugParticleReceiving=%s\n", debug::valueOf(_debugParticleReceiving));
const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug";
_shouldShowAnimationDebug = cmdOptionExists(_argc, _argv, WANT_ANIMATION_DEBUG);
qDebug("shouldShowAnimationDebug=%s\n", debug::valueOf(_shouldShowAnimationDebug));
// By default we will particle persist, if you want to disable this, then pass in this parameter
const char* NO_PARTICLE_PERSIST = "--NoParticlePersist";
if (cmdOptionExists(_argc, _argv, NO_PARTICLE_PERSIST)) {
_wantParticlePersist = false;
}
qDebug("wantParticlePersist=%s\n", debug::valueOf(_wantParticlePersist));
// if we want Particle Persistence, set up the local file and persist thread
if (_wantParticlePersist) {
// Check to see if the user passed in a command line option for setting packet send rate
const char* PARTICLES_PERSIST_FILENAME = "--particlesPersistFilename";
const char* particlesPersistFilenameParameter = getCmdOption(_argc, _argv, PARTICLES_PERSIST_FILENAME);
if (particlesPersistFilenameParameter) {
strcpy(_particlePersistFilename, particlesPersistFilenameParameter);
} else {
//strcpy(particlePersistFilename, _wantLocalDomain ? LOCAL_PARTICLES_PERSIST_FILE : PARTICLES_PERSIST_FILE);
strcpy(_particlePersistFilename, LOCAL_PARTICLES_PERSIST_FILE);
}
qDebug("particlePersistFilename=%s\n", _particlePersistFilename);
// now set up ParticlePersistThread
_particlePersistThread = new ParticlePersistThread(&_serverTree, _particlePersistFilename);
if (_particlePersistThread) {
_particlePersistThread->initialize(true);
}
}
// Check to see if the user passed in a command line option for loading an old style local
// Particle File. If so, load it now. This is not the same as a particle persist file
const char* INPUT_FILE = "-i";
const char* particlesFilename = getCmdOption(_argc, _argv, INPUT_FILE);
if (particlesFilename) {
_serverTree.readFromSVOFile(particlesFilename);
}
// 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) {
_packetsPerClientPerInterval = atoi(packetsPerSecond) / INTERVALS_PER_SECOND;
if (_packetsPerClientPerInterval < 1) {
_packetsPerClientPerInterval = 1;
}
qDebug("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, _packetsPerClientPerInterval);
}
sockaddr senderAddress;
unsigned char* packetData = new unsigned char[MAX_PACKET_SIZE];
ssize_t packetLength;
timeval lastDomainServerCheckIn = {};
// set up our jurisdiction broadcaster...
_jurisdictionSender = new JurisdictionSender(_jurisdiction);
if (_jurisdictionSender) {
_jurisdictionSender->initialize(true);
}
// set up our ParticleSeverPacketProcessor
_particleServerPacketProcessor = new ParticleSeverPacketProcessor(this);
if (_particleServerPacketProcessor) {
_particleServerPacketProcessor->initialize(true);
}
// Convert now to tm struct for local timezone
tm* localtm = localtime(&_started);
const int MAX_TIME_LENGTH = 128;
char localBuffer[MAX_TIME_LENGTH] = { 0 };
char utcBuffer[MAX_TIME_LENGTH] = { 0 };
strftime(localBuffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", localtm);
// Convert now to tm struct for UTC
tm* gmtm = gmtime(&_started);
if (gmtm != NULL) {
strftime(utcBuffer, MAX_TIME_LENGTH, " [%m/%d/%Y %X UTC]", gmtm);
}
qDebug() << "Now running... started at: " << localBuffer << utcBuffer << "\n";
// loop to send to nodes requesting data
while (true) {
// check for >= in case one gets past the goalie
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
qDebug() << "Exit loop... getInstance()->getNumNoReplyDomainCheckIns() >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS\n";
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();
}
// ping our inactive nodes to punch holes with them
nodeList->possiblyPingInactiveNodes();
if (nodeList->getNodeSocket()->receive(&senderAddress, packetData, &packetLength) &&
packetVersionMatch(packetData)) {
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
if (packetData[0] == PACKET_TYPE_PARTICLE_QUERY) {
bool debug = false;
if (debug) {
qDebug("Got PACKET_TYPE_PARTICLE_QUERY at %llu.\n", usecTimestampNow());
}
// If we got a PACKET_TYPE_PARTICLE_QUERY, then we're talking to an NODE_TYPE_AVATAR, and we
// need to make sure we have it in our nodeList.
QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*)packetData + numBytesPacketHeader,
NUM_BYTES_RFC4122_UUID));
Node* node = nodeList->nodeWithUUID(nodeUUID);
if (node) {
nodeList->updateNodeWithData(node, &senderAddress, packetData, packetLength);
if (!node->getActiveSocket()) {
// we don't have an active socket for this node, but they're talking to us
// this means they've heard from us and can reply, let's assume public is active
node->activatePublicSocket();
}
ParticleReceiverData* nodeData = (ParticleReceiverData*) node->getLinkedData();
if (nodeData && !nodeData->isParticleSendThreadInitalized()) {
nodeData->initializeParticleSendThread(this);
}
}
} else if (packetData[0] == PACKET_TYPE_PARTICLE_JURISDICTION_REQUEST) {
if (_jurisdictionSender) {
_jurisdictionSender->queueReceivedPacket(senderAddress, packetData, packetLength);
}
} else if (_particleServerPacketProcessor &&
(packetData[0] == PACKET_TYPE_SET_VOXEL
|| packetData[0] == PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE
|| packetData[0] == PACKET_TYPE_ERASE_VOXEL)) {
const char* messageName;
switch (packetData[0]) {
case PACKET_TYPE_SET_VOXEL:
messageName = "PACKET_TYPE_SET_VOXEL";
break;
case PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE:
messageName = "PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE";
break;
case PACKET_TYPE_ERASE_VOXEL:
messageName = "PACKET_TYPE_ERASE_VOXEL";
break;
}
_particleServerPacketProcessor->queueReceivedPacket(senderAddress, packetData, packetLength);
} else {
// let processNodeData handle it.
NodeList::getInstance()->processNodeData(&senderAddress, packetData, packetLength);
}
}
}
// call NodeList::clear() so that all of our node specific objects, including our sending threads, are
// properly shutdown and cleaned up.
NodeList::getInstance()->clear();
if (_jurisdictionSender) {
_jurisdictionSender->terminate();
delete _jurisdictionSender;
}
if (_particleServerPacketProcessor) {
_particleServerPacketProcessor->terminate();
delete _particleServerPacketProcessor;
}
if (_particlePersistThread) {
_particlePersistThread->terminate();
delete _particlePersistThread;
}
// tell our NodeList we're done with notifications
nodeList->removeHook(&_nodeWatcher);
delete _jurisdiction;
_jurisdiction = NULL;
qDebug() << "ParticleSever::run()... DONE\n";
}
void ParticleSever::nodeAdded(Node* node) {
// do nothing
}
void ParticleSever::nodeKilled(Node* node) {
// Use this to cleanup our node
if (node->getType() == NODE_TYPE_AGENT) {
ParticleReceiverData* nodeData = (ParticleReceiverData*)node->getLinkedData();
if (nodeData) {
node->setLinkedData(NULL);
delete nodeData;
}
}
};

View file

@ -1,91 +0,0 @@
//
// ParticleServer.h
// particle-server
//
// Created by Brad Hefta-Gaub on 12/2/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __particle_server__ParticleServer__
#define __particle_server__ParticleServer__
#include <QStringList>
#include <QDateTime>
#include <QtCore/QCoreApplication>
#include <Assignment.h>
#include "civetweb.h"
#include "ParticlePersistThread.h"
#include "ParticleSendThread.h"
#include "ParticleServerConsts.h"
#include "ParticleServerPacketProcessor.h"
/// Handles assignments of type ParticleServer - sending particles to various clients.
class ParticleServer : public Assignment {
public:
ParticleServer(const unsigned char* dataBuffer, int numBytes);
~ParticleServer();
/// runs the particle server assignment
void run();
/// allows setting of run arguments
void setArguments(int argc, char** argv);
bool wantsDebugParticleSending() const { return _debugParticleSending; }
bool wantsDebugParticleReceiving() const { return _debugParticleReceiving; }
bool wantsVerboseDebug() const { return _verboseDebug; }
bool wantShowAnimationDebug() const { return _shouldShowAnimationDebug; }
bool wantDumpParticlesOnMove() const { return _dumpParticlesOnMove; }
bool wantDisplayParticleStats() const { return _displayParticleStats; }
ParticleTree& getServerTree() { return _serverTree; }
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; }
static ParticleServer* GetInstance() { return _theInstance; }
bool isInitialLoadComplete() const { return (_particlePersistThread) ? _particlePersistThread->isInitialLoadComplete() : true; }
time_t* getLoadCompleted() { return (_particlePersistThread) ? _particlePersistThread->getLoadCompleted() : NULL; }
uint64_t getLoadElapsedTime() const { return (_particlePersistThread) ? _particlePersistThread->getLoadElapsedTime() : 0; }
private:
int _argc;
const char** _argv;
char** _parsedArgV;
char _particlePersistFilename[MAX_FILENAME_LENGTH];
int _packetsPerClientPerInterval;
ParticleTree _serverTree; // this IS a reaveraging tree
bool _wantParticlePersist;
bool _wantLocalDomain;
bool _debugParticleSending;
bool _shouldShowAnimationDebug;
bool _displayParticleStats;
bool _debugParticleReceiving;
bool _dumpParticlesOnMove;
bool _verboseDebug;
JurisdictionMap* _jurisdiction;
JurisdictionSender* _jurisdictionSender;
ParticleServerPacketProcessor* _particleServerPacketProcessor;
ParticlePersistThread* _particlePersistThread;
NodeWatcher _nodeWatcher; // used to cleanup AGENT data when agents are killed
void parsePayload();
void initMongoose(int port);
static int civetwebRequestHandler(struct mg_connection *connection);
static ParticleServer* _theInstance;
time_t _started;
uint64_t _startedUSecs;
};
#endif // __particle_server__ParticleServer__

View file

@ -1,28 +0,0 @@
// ParticleServerConsts.h
// particle-server
//
// Created by Brad Hefta-Gaub on 12/2/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __particle_server__ParticleServerConsts__
#define __particle_server__ParticleServerConsts__
#include <SharedUtil.h>
#include <NodeList.h> // for MAX_PACKET_SIZE
#include <JurisdictionSender.h>
#include <ParticleTree.h>
#include "ParticleServerPacketProcessor.h"
const int MAX_FILENAME_LENGTH = 1024;
const int INTERVALS_PER_SECOND = 60;
const int PARTICLE_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND;
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating particles
const int ENVIRONMENT_SEND_INTERVAL_USECS = 1000000;
extern const char* LOCAL_PARTICLES_PERSIST_FILE;
extern const char* PARTICLES_PERSIST_FILE;
#endif // __particle_server__ParticleServerConsts__

View file

@ -1,222 +0,0 @@
//
// ParticleServerPacketProcessor.cpp
// particle-server
//
// Created by Brad Hefta-Gaub on 12/2/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded network packet processor for the particle-server
//
#include <PacketHeaders.h>
#include <PerfStat.h>
#include "ParticleServer.h"
#include "ParticleServerConsts.h"
#include "ParticleServerPacketProcessor.h"
static QUuid DEFAULT_NODE_ID_REF;
ParticleServerPacketProcessor::ParticleServerPacketProcessor(ParticleServer* myServer) :
_myServer(myServer),
_receivedPacketCount(0),
_totalTransitTime(0),
_totalProcessTime(0),
_totalLockWaitTime(0),
_totalParticlesInPacket(0),
_totalPackets(0)
{
}
void ParticleServerPacketProcessor::resetStats() {
_totalTransitTime = 0;
_totalProcessTime = 0;
_totalLockWaitTime = 0;
_totalParticlesInPacket = 0;
_totalPackets = 0;
_singleSenderStats.clear();
}
void ParticleServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
bool debugProcessPacket = _myServer->wantsVerboseDebug();
if (debugProcessPacket) {
printf("ParticleServerPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength);
}
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
if (packetData[0] == PACKET_TYPE_SET_PARTICLE || packetData[0] == PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE) {
bool destructive = (packetData[0] == PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE);
PerformanceWarning warn(_myServer->wantShowAnimationDebug(),
destructive ? "PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE" : "PACKET_TYPE_SET_PARTICLE",
_myServer->wantShowAnimationDebug());
_receivedPacketCount++;
unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence))));
uint64_t arrivedAt = usecTimestampNow();
uint64_t transitTime = arrivedAt - sentAt;
int particlesInPacket = 0;
uint64_t processTime = 0;
uint64_t lockWaitTime = 0;
if (_myServer->wantShowAnimationDebug() || _myServer->wantsDebugParticleReceiving()) {
printf("PROCESSING THREAD: got %s - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n",
destructive ? "PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE" : "PACKET_TYPE_SET_PARTICLE",
_receivedPacketCount, packetLength, sequence, transitTime);
}
int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt);
unsigned char* particleData = (unsigned char*)&packetData[atByte];
while (atByte < packetLength) {
int maxSize = packetLength - atByte;
if (debugProcessPacket) {
printf("ParticleServerPacketProcessor::processPacket() %s packetData=%p packetLength=%ld particleData=%p atByte=%d maxSize=%d\n",
destructive ? "PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE" : "PACKET_TYPE_SET_PARTICLE",
packetData, packetLength, particleData, atByte, maxSize);
}
int octets = numberOfThreeBitSectionsInCode(particleData, maxSize);
if (octets == OVERFLOWED_OCTCODE_BUFFER) {
printf("WARNING! Got particle edit record that would overflow buffer in numberOfThreeBitSectionsInCode(), ");
printf("bailing processing of packet!\n");
break;
}
const int COLOR_SIZE_IN_BYTES = 3;
int particleDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES;
int particleCodeSize = bytesRequiredForCodeLength(octets);
if (atByte + particleDataSize <= packetLength) {
if (_myServer->wantShowAnimationDebug()) {
int red = particleData[particleCodeSize + RED_INDEX];
int green = particleData[particleCodeSize + GREEN_INDEX];
int blue = particleData[particleCodeSize + BLUE_INDEX];
float* vertices = firstVertexForCode(particleData);
printf("inserting particle: %f,%f,%f r=%d,g=%d,b=%d\n", vertices[0], vertices[1], vertices[2], red, green, blue);
delete[] vertices;
}
uint64_t startLock = usecTimestampNow();
_myServer->getServerTree().lockForWrite();
uint64_t startProcess = usecTimestampNow();
_myServer->getServerTree().readCodeColorBufferToTree(particleData, destructive);
_myServer->getServerTree().unlock();
uint64_t endProcess = usecTimestampNow();
particlesInPacket++;
uint64_t thisProcessTime = endProcess - startProcess;
uint64_t thisLockWaitTime = startProcess - startLock;
processTime += thisProcessTime;
lockWaitTime += thisLockWaitTime;
// skip to next particle edit record in the packet
particleData += particleDataSize;
atByte += particleDataSize;
} else {
printf("WARNING! Got particle edit record that would overflow buffer, bailing processing of packet!\n");
break;
}
}
if (debugProcessPacket) {
printf("ParticleServerPacketProcessor::processPacket() DONE LOOPING FOR %s packetData=%p packetLength=%ld particleData=%p atByte=%d\n",
destructive ? "PACKET_TYPE_SET_PARTICLE_DESTRUCTIVE" : "PACKET_TYPE_SET_PARTICLE",
packetData, packetLength, particleData, atByte);
}
// Make sure our Node and NodeList knows we've heard from this node.
Node* senderNode = NodeList::getInstance()->nodeWithAddress(&senderAddress);
QUuid& nodeUUID = DEFAULT_NODE_ID_REF;
if (senderNode) {
senderNode->setLastHeardMicrostamp(usecTimestampNow());
nodeUUID = senderNode->getUUID();
if (debugProcessPacket) {
qDebug() << "sender has uuid=" << nodeUUID << "\n";
}
} else {
if (debugProcessPacket) {
qDebug() << "sender has no known nodeUUID.\n";
}
}
trackInboundPackets(nodeUUID, sequence, transitTime, particlesInPacket, processTime, lockWaitTime);
} else if (packetData[0] == PACKET_TYPE_ERASE_PARTICLE) {
_receivedPacketCount++;
unsigned short int sequence = (*((unsigned short int*)(packetData + numBytesPacketHeader)));
uint64_t sentAt = (*((uint64_t*)(packetData + numBytesPacketHeader + sizeof(sequence))));
uint64_t arrivedAt = usecTimestampNow();
uint64_t transitTime = arrivedAt - sentAt;
if (_myServer->wantShowAnimationDebug() || _myServer->wantsDebugParticleReceiving()) {
printf("PROCESSING THREAD: got PACKET_TYPE_ERASE_PARTICLE - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n",
_receivedPacketCount, packetLength, sequence, transitTime);
}
// Send these bits off to the ParticleTree class to process them
_myServer->getServerTree().lockForWrite();
_myServer->getServerTree().processRemoveParticleBitstream((unsigned char*)packetData, packetLength);
_myServer->getServerTree().unlock();
// Make sure our Node and NodeList knows we've heard from this node.
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);
if (node) {
node->setLastHeardMicrostamp(usecTimestampNow());
}
} else {
printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]);
}
}
void ParticleServerPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
int particlesInPacket, uint64_t processTime, uint64_t lockWaitTime) {
_totalTransitTime += transitTime;
_totalProcessTime += processTime;
_totalLockWaitTime += lockWaitTime;
_totalParticlesInPacket += particlesInPacket;
_totalPackets++;
// find the individual senders stats and track them there too...
// see if this is the first we've heard of this node...
if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) {
SingleSenderStats stats;
stats._totalTransitTime += transitTime;
stats._totalProcessTime += processTime;
stats._totalLockWaitTime += lockWaitTime;
stats._totalParticlesInPacket += particlesInPacket;
stats._totalPackets++;
_singleSenderStats[nodeUUID] = stats;
} else {
SingleSenderStats& stats = _singleSenderStats[nodeUUID];
stats._totalTransitTime += transitTime;
stats._totalProcessTime += processTime;
stats._totalLockWaitTime += lockWaitTime;
stats._totalParticlesInPacket += particlesInPacket;
stats._totalPackets++;
}
}
SingleSenderStats::SingleSenderStats() {
_totalTransitTime = 0;
_totalProcessTime = 0;
_totalLockWaitTime = 0;
_totalParticlesInPacket = 0;
_totalPackets = 0;
}

View file

@ -1,83 +0,0 @@
//
// ParticleServerPacketProcessor.h
// particle-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded network packet processor for the particle-server
//
#ifndef __particle_server__ParticleServerPacketProcessor__
#define __particle_server__ParticleServerPacketProcessor__
#include <map>
#include <ReceivedPacketProcessor.h>
class ParticleServer;
class SingleSenderStats {
public:
SingleSenderStats();
uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; }
uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; }
uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; }
uint64_t getTotalParticlesProcessed() const { return _totalParticlesInPacket; }
uint64_t getTotalPacketsProcessed() const { return _totalPackets; }
uint64_t getAverageProcessTimePerParticle() const
{ return _totalParticlesInPacket == 0 ? 0 : _totalProcessTime / _totalParticlesInPacket; }
uint64_t getAverageLockWaitTimePerParticle() const
{ return _totalParticlesInPacket == 0 ? 0 : _totalLockWaitTime / _totalParticlesInPacket; }
uint64_t _totalTransitTime;
uint64_t _totalProcessTime;
uint64_t _totalLockWaitTime;
uint64_t _totalParticlesInPacket;
uint64_t _totalPackets;
};
typedef std::map<QUuid, SingleSenderStats> NodeToSenderStatsMap;
typedef std::map<QUuid, SingleSenderStats>::iterator NodeToSenderStatsMapIterator;
/// Handles processing of incoming network packets for the particle-server. As with other ReceivedPacketProcessor classes
/// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket()
class ParticleServerPacketProcessor : public ReceivedPacketProcessor {
public:
ParticleServerPacketProcessor(ParticleServer* myServer);
uint64_t getAverageTransitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalTransitTime / _totalPackets; }
uint64_t getAverageProcessTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalProcessTime / _totalPackets; }
uint64_t getAverageLockWaitTimePerPacket() const { return _totalPackets == 0 ? 0 : _totalLockWaitTime / _totalPackets; }
uint64_t getTotalParticlesProcessed() const { return _totalParticlesInPacket; }
uint64_t getTotalPacketsProcessed() const { return _totalPackets; }
uint64_t getAverageProcessTimePerParticle() const
{ return _totalParticlesInPacket == 0 ? 0 : _totalProcessTime / _totalParticlesInPacket; }
uint64_t getAverageLockWaitTimePerParticle() const
{ return _totalParticlesInPacket == 0 ? 0 : _totalLockWaitTime / _totalParticlesInPacket; }
void resetStats();
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; }
protected:
virtual void processPacket(sockaddr& senderAddress, unsigned char* packetData, ssize_t packetLength);
private:
void trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
int particlesInPacket, uint64_t processTime, uint64_t lockWaitTime);
ParticleServer* _myServer;
int _receivedPacketCount;
uint64_t _totalTransitTime;
uint64_t _totalProcessTime;
uint64_t _totalLockWaitTime;
uint64_t _totalParticlesInPacket;
uint64_t _totalPackets;
NodeToSenderStatsMap _singleSenderStats;
};
#endif // __particle_server__ParticleServerPacketProcessor__

View file

@ -1,28 +0,0 @@
cmake_minimum_required(VERSION 2.8)
set(ROOT_DIR ../..)
set(MACRO_DIR ${ROOT_DIR}/cmake/macros)
# setup for find modules
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/")
set(TARGET_NAME particles)
find_package(Qt5Widgets REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME})
qt5_use_modules(${TARGET_NAME} Widgets)
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
# link ZLIB
find_package(ZLIB)
include_directories(${ZLIB_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})

View file

@ -1 +0,0 @@
// ParticleTree.cpp