This commit is contained in:
Andrzej Kapolka 2013-11-07 16:21:06 -08:00
commit dabb670f1a
25 changed files with 307 additions and 155 deletions

View file

@ -57,7 +57,7 @@ void Agent::run() {
NodeList* nodeList = NodeList::getInstance();
nodeList->setOwnerType(NODE_TYPE_AGENT);
const char AGENT_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AUDIO_MIXER };
const char AGENT_NODE_TYPES_OF_INTEREST[1] = { NODE_TYPE_VOXEL_SERVER };
nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST));
@ -114,9 +114,11 @@ void Agent::run() {
QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE));
engine.globalObject().setProperty("TREE_SCALE", treeScaleValue);
const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000;
// let the VoxelPacketSender know how frequently we plan to call it
voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(INJECT_INTERVAL_USECS);
voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS);
// hook in a constructor for audio injectorss
AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL);
@ -158,28 +160,14 @@ void Agent::run() {
NodeList::getInstance()->sendDomainServerCheckIn();
}
// find the audio-mixer in the NodeList so we can inject audio at it
Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER);
if (audioMixer && audioMixer->getActiveSocket()) {
emit willSendAudioDataCallback();
}
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow();
int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow();
if (usecToSleep > 0) {
usleep(usecToSleep);
}
if (audioMixer && NodeList::getInstance()->getNodeActiveSocketOrPing(audioMixer) && scriptedAudioInjector.hasSamplesToInject()) {
// we have an audio mixer and samples to inject, send those off
scriptedAudioInjector.injectAudio(NodeList::getInstance()->getNodeSocket(), audioMixer->getActiveSocket());
// clear out the audio injector so that it doesn't re-send what we just sent
scriptedAudioInjector.clear();
}
if (voxelScripter.getVoxelPacketSender()->voxelServersExist()) {
timeval thisSend = {};
gettimeofday(&thisSend, NULL);
// allow the scripter's call back to setup visual data
emit willSendVisualDataCallback();
@ -188,14 +176,13 @@ void Agent::run() {
// since we're in non-threaded mode, call process so that the packets are sent
voxelScripter.getVoxelPacketSender()->process();
}
if (engine.hasUncaughtException()) {
int line = engine.uncaughtExceptionLineNumber();
qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n";
}
while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)
&& packetVersionMatch(receivedData)) {
if (receivedData[0] == PACKET_TYPE_VOXEL_JURISDICTION) {

View file

@ -12,9 +12,6 @@
#include <sys/time.h>
#include <sys/wait.h>
#include <QtCore/QCoreApplication>
#include <Logging.h>
#include <NodeList.h>
#include <PacketHeaders.h>

View file

@ -78,6 +78,11 @@ function updateCells() {
nextCells[i][j] = -1;
}
}
if (Math.random() < 0.001) {
// Random mutation to keep things interesting in there.
nextCells[i][j] = 1;
}
}
}

View file

@ -4094,11 +4094,13 @@ void Application::nodeKilled(Node* node) {
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
// Add the jurisditionDetails object to the list of "fade outs"
VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE);
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade);
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) {
VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE);
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade);
}
}
} else if (node->getLinkedData() == _lookatTargetAvatar) {
_lookatTargetAvatar = NULL;
@ -4127,11 +4129,13 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng
rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s);
// Add the jurisditionDetails object to the list of "fade outs"
VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE);
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade);
if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) {
VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE);
fade.voxelDetails = rootDetails;
const float slightly_smaller = 0.99;
fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller;
_voxelFades.push_back(fade);
}
}
// store jurisdiction details for later use
// This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it

View file

@ -22,6 +22,7 @@
#include <QSlider>
#include <QStandardPaths>
#include <QUuid>
#include <QWindow>
#include <UUID.h>
@ -282,6 +283,7 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures);
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion);
addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges);
addActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools()));
QMenu* cullingOptionsMenu = voxelOptionsMenu->addMenu("Culling Options");
@ -718,16 +720,13 @@ void Menu::aboutApp() {
InfoView::forcedShow();
}
void updateDSHostname(const QString& domainServerHostname) {
QString newHostname(DEFAULT_DOMAIN_HOSTNAME);
void sendFakeEnterEvent() {
QPoint lastCursorPosition = QCursor::pos();
QGLWidget* glWidget = Application::getInstance()->getGLWidget();
if (domainServerHostname.size() > 0) {
// the user input a new hostname, use that
newHostname = domainServerHostname;
}
// give our nodeList the new domain-server hostname
NodeList::getInstance()->setDomainHostname(newHostname);
QPoint windowPosition = glWidget->mapFromGlobal(lastCursorPosition);
QEnterEvent enterEvent = QEnterEvent(windowPosition, windowPosition, lastCursorPosition);
QCoreApplication::sendEvent(glWidget, &enterEvent);
}
const int QLINE_MINIMUM_WIDTH = 400;
@ -743,11 +742,15 @@ void Menu::login() {
loginDialog.resize(loginDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, loginDialog.size().height());
int dialogReturn = loginDialog.exec();
if (dialogReturn == QDialog::Accepted && !loginDialog.textValue().isEmpty() && loginDialog.textValue() != username) {
// there has been a username change
// ask for a profile reset with the new username
Application::getInstance()->resetProfile(loginDialog.textValue());
}
sendFakeEnterEvent();
}
void Menu::editPreferences() {
@ -811,52 +814,52 @@ void Menu::editPreferences() {
layout->addWidget(buttons);
int ret = dialog.exec();
if (ret != QDialog::Accepted) {
return;
}
QUrl faceModelURL(faceURLEdit->text());
if (faceModelURL.toString() != faceURLString) {
// change the faceModelURL in the profile, it will also update this user's BlendFace
applicationInstance->getProfile()->setFaceModelURL(faceModelURL);
if (ret == QDialog::Accepted) {
QUrl faceModelURL(faceURLEdit->text());
// send the new face mesh URL to the data-server (if we have a client UUID)
DataServerClient::putValueForKey(DataServerKey::FaceMeshURL,
faceModelURL.toString().toLocal8Bit().constData());
}
QUrl skeletonModelURL(skeletonURLEdit->text());
if (skeletonModelURL.toString() != skeletonURLString) {
// change the skeletonModelURL in the profile, it will also update this user's Body
applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL);
if (faceModelURL.toString() != faceURLString) {
// change the faceModelURL in the profile, it will also update this user's BlendFace
applicationInstance->getProfile()->setFaceModelURL(faceModelURL);
// send the new face mesh URL to the data-server (if we have a client UUID)
DataServerClient::putValueForKey(DataServerKey::FaceMeshURL,
faceModelURL.toString().toLocal8Bit().constData());
}
// send the new skeleton model URL to the data-server (if we have a client UUID)
DataServerClient::putValueForKey(DataServerKey::SkeletonURL,
skeletonModelURL.toString().toLocal8Bit().constData());
QUrl skeletonModelURL(skeletonURLEdit->text());
if (skeletonModelURL.toString() != skeletonURLString) {
// change the skeletonModelURL in the profile, it will also update this user's Body
applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL);
// send the new skeleton model URL to the data-server (if we have a client UUID)
DataServerClient::putValueForKey(DataServerKey::SkeletonURL,
skeletonModelURL.toString().toLocal8Bit().constData());
}
QUrl avatarVoxelURL(avatarURL->text());
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
Avatar::sendAvatarURLsMessage(avatarVoxelURL);
applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
_maxVoxels = maxVoxels->value();
applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels);
applicationInstance->getAvatar()->setLeanScale(leanScale->value());
_audioJitterBufferSamples = audioJitterBufferSamples->value();
if (_audioJitterBufferSamples != 0) {
applicationInstance->getAudio()->setJitterBufferSamples(_audioJitterBufferSamples);
}
_fieldOfView = fieldOfView->value();
applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height());
}
QUrl avatarVoxelURL(avatarURL->text());
applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL);
Avatar::sendAvatarURLsMessage(avatarVoxelURL);
applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum());
_maxVoxels = maxVoxels->value();
applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels);
applicationInstance->getAvatar()->setLeanScale(leanScale->value());
_audioJitterBufferSamples = audioJitterBufferSamples->value();
if (_audioJitterBufferSamples != 0) {
applicationInstance->getAudio()->setJitterBufferSamples(_audioJitterBufferSamples);
}
_fieldOfView = fieldOfView->value();
applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height());
sendFakeEnterEvent();
}
void Menu::goToDomain() {
@ -876,9 +879,19 @@ void Menu::goToDomain() {
domainDialog.resize(domainDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, domainDialog.size().height());
int dialogReturn = domainDialog.exec();
if (dialogReturn == QDialog::Accepted && !domainDialog.textValue().isEmpty()) {
updateDSHostname(domainDialog.textValue());
if (dialogReturn == QDialog::Accepted) {
QString newHostname(DEFAULT_DOMAIN_HOSTNAME);
if (domainDialog.textValue().size() > 0) {
// the user input a new hostname, use that
newHostname = domainDialog.textValue();
}
// give our nodeList the new domain-server hostname
NodeList::getInstance()->setDomainHostname(domainDialog.textValue());
}
sendFakeEnterEvent();
}
void Menu::goToLocation() {
@ -918,6 +931,8 @@ void Menu::goToLocation() {
}
}
}
sendFakeEnterEvent();
}
void Menu::goToUser() {
@ -935,6 +950,8 @@ void Menu::goToUser() {
DataServerClient::getValuesForKeysAndUserString((QStringList() << DataServerKey::Domain << DataServerKey::Position),
userDialog.textValue());
}
sendFakeEnterEvent();
}
void Menu::bandwidthDetails() {

View file

@ -165,6 +165,7 @@ namespace MenuOption {
const QString EchoAudio = "Echo Audio";
const QString ExportVoxels = "Export Voxels";
const QString ExtraDebugging = "Extra Debugging";
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
const QString HeadMouse = "Head Mouse";
const QString FaceMode = "Cycle Face Mode";
const QString FaceshiftTCP = "Faceshift (TCP)";

View file

@ -2645,23 +2645,8 @@ bool VoxelSystem::killSourceVoxelsOperation(VoxelNode* node, void* extraData) {
void VoxelSystem::nodeKilled(Node* node) {
if (node->getType() == NODE_TYPE_VOXEL_SERVER) {
_voxelServerCount--;
QUuid nodeUUID = node->getUUID();
qDebug("VoxelSystem... voxel server %s removed...\n", nodeUUID.toString().toLocal8Bit().constData());
if (_voxelServerCount > 0) {
// Kill any voxels from the local tree that match this nodeID
// commenting out for removal of 16 bit node IDs
lockTree();
_tree->recurseTreeWithOperation(killSourceVoxelsOperation, &nodeUUID);
unlockTree();
_tree->setDirtyBit();
setupNewVoxelsForDrawing();
} else {
// Last server, take the easy way and kill all the local voxels!
killLocalVoxels();
}
}
}

View file

@ -255,6 +255,11 @@ Avatar::~Avatar() {
delete _balls;
}
void Avatar::deleteOrDeleteLater() {
this->deleteLater();
}
void Avatar::init() {
_head.init();
_hand.init();

View file

@ -134,6 +134,7 @@ public:
Avatar(Node* owningNode = NULL);
~Avatar();
void deleteOrDeleteLater();
void init();
void simulate(float deltaTime, Transmitter* transmitter);

View file

@ -44,7 +44,7 @@ Node::~Node() {
delete _localSocket;
if (_linkedData) {
_linkedData->deleteLater();
_linkedData->deleteOrDeleteLater();
}
delete _bytesReceivedMovingAverage;

View file

@ -16,4 +16,8 @@ NodeData::NodeData(Node* owningNode) :
NodeData::~NodeData() {
}
void NodeData::deleteOrDeleteLater() {
delete this;
}

View file

@ -21,6 +21,8 @@ public:
virtual ~NodeData() = 0;
virtual int parseData(unsigned char* sourceBuffer, int numBytes) = 0;
virtual void deleteOrDeleteLater();
Node* getOwningNode() { return _owningNode; }
protected:
Node* _owningNode;

View file

@ -35,6 +35,8 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
_lastVoxelPacketLength = 0;
_duplicatePacketCount = 0;
resetVoxelPacket();
qDebug("VoxelNodeData::VoxelNodeData() this=%p owningNode=%p\n", this, owningNode);
}
void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) {
@ -42,6 +44,10 @@ void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) {
QUuid nodeUUID = getOwningNode()->getUUID();
_voxelSendThread = new VoxelSendThread(nodeUUID, voxelServer);
_voxelSendThread->initialize(true);
qDebug("VoxelNodeData::initializeVoxelSendThread() this=%p owningNode=%p _voxelSendThread=%p\n",
this, getOwningNode(), _voxelSendThread);
qDebug() << "VoxelNodeData::initializeVoxelSendThread() nodeUUID=" << nodeUUID << "\n";
}
bool VoxelNodeData::packetIsDuplicate() const {
@ -111,12 +117,19 @@ void VoxelNodeData::writeToPacket(unsigned char* buffer, int bytes) {
}
VoxelNodeData::~VoxelNodeData() {
qDebug("VoxelNodeData::~VoxelNodeData() this=%p owningNode=%p _voxelSendThread=%p\n",
this, getOwningNode(), _voxelSendThread);
QUuid nodeUUID = getOwningNode()->getUUID();
qDebug() << "VoxelNodeData::~VoxelNodeData() nodeUUID=" << nodeUUID << "\n";
delete[] _voxelPacket;
delete[] _lastVoxelPacket;
if (_voxelSendThread) {
_voxelSendThread->terminate();
delete _voxelSendThread;
qDebug("VoxelNodeData::~VoxelNodeData() DELETED _voxelSendThread=%p\n", _voxelSendThread);
}
}

View file

@ -24,7 +24,7 @@ class VoxelServer;
class VoxelNodeData : public VoxelQuery {
public:
VoxelNodeData(Node* owningNode);
~VoxelNodeData();
virtual ~VoxelNodeData();
void resetVoxelPacket(); // resets voxel packet to after "V" header

View file

@ -20,30 +20,28 @@ VoxelPersistThread::VoxelPersistThread(VoxelTree* tree, const char* filename, in
_tree(tree),
_filename(filename),
_persistInterval(persistInterval),
_initialLoad(false) {
_initialLoadComplete(false),
_loadTimeUSecs(0) {
}
bool VoxelPersistThread::process() {
if (!_initialLoad) {
_initialLoad = true;
if (!_initialLoadComplete) {
uint64_t loadStarted = usecTimestampNow();
qDebug("loading voxels from file: %s...\n", _filename);
bool persistantFileRead;
_tree->lockForWrite();
{
PerformanceWarning warn(true, "Loading Voxel File", true);
persistantFileRead = _tree->readFromSVOFile(_filename);
}
_tree->unlock();
if (persistantFileRead) {
PerformanceWarning warn(true, "Voxels Re-Averaging", true);
// after done inserting all these voxels, then reaverage colors
qDebug("BEGIN Voxels Re-Averaging\n");
_tree->reaverageVoxelColors(_tree->rootNode);
qDebug("DONE WITH Voxels Re-Averaging\n");
}
_loadCompleted = time(0);
uint64_t loadDone = usecTimestampNow();
_loadTimeUSecs = loadDone - loadStarted;
_tree->clearDirtyBit(); // the tree is clean since we just loaded it
qDebug("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
@ -61,6 +59,7 @@ bool VoxelPersistThread::process() {
qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n",
VoxelNode::getSetChildAtIndexTime(), VoxelNode::getSetChildAtIndexCalls(), usecPerSet);
_initialLoadComplete = true;
}
uint64_t MSECS_TO_USECS = 1000;

View file

@ -21,6 +21,12 @@ public:
static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
VoxelPersistThread(VoxelTree* 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();
@ -28,7 +34,10 @@ private:
VoxelTree* _tree;
const char* _filename;
int _persistInterval;
bool _initialLoad;
bool _initialLoadComplete;
time_t _loadCompleted;
uint64_t _loadTimeUSecs;
};
#endif // __voxel_server__VoxelPersistThread__

View file

@ -26,28 +26,39 @@ VoxelSendThread::VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer) :
bool VoxelSendThread::process() {
uint64_t start = usecTimestampNow();
Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID);
VoxelNodeData* nodeData = NULL;
// don't do any send processing until the initial load of the voxels is complete...
if (_myServer->isInitialLoadComplete()) {
Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID);
if (node) {
nodeData = (VoxelNodeData*) node->getLinkedData();
}
if (node) {
node->lock(); // make sure the node list doesn't kill our node while we're using it
VoxelNodeData* nodeData = NULL;
int packetsSent = 0;
nodeData = (VoxelNodeData*) 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->wantsDebugVoxelSending()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged);
}
// 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->wantsDebugVoxelSending()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged);
}
node->unlock(); // we're done with this node for now.
}
} else {
if (_myServer->wantsDebugVoxelSending()) {
qDebug("VoxelSendThread::process() waiting for isInitialLoadComplete()\n");
}
}
// dynamically sleep until we need to fire off the next set of voxels
int elapsed = (usecTimestampNow() - start);
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed;
if (usecToSleep > 0) {
usleep(usecToSleep);
} else {
@ -55,6 +66,7 @@ bool VoxelSendThread::process() {
std::cout << "Last send took too much time, not sleeping!\n";
}
}
return isStillRunning(); // keep running till they terminate us
}
@ -112,8 +124,6 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
/// Version of voxel distributor that sends the deepest LOD level at once
int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) {
_myServer->lockTree();
int truePacketsSent = 0;
int trueBytesSent = 0;
int packetsSentThisInterval = 0;
@ -284,10 +294,13 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
nodeData->getLastTimeBagEmpty(),
isFullScene, &nodeData->stats, _myServer->getJurisdiction());
_myServer->getServerTree().lockForRead();
nodeData->stats.encodeStarted();
bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, _tempOutputBuffer, MAX_VOXEL_PACKET_SIZE - 1,
nodeData->nodeBag, params);
nodeData->stats.encodeStopped();
_myServer->getServerTree().unlock();
if (nodeData->getAvailable() >= bytesWritten) {
nodeData->writeToPacket(_tempOutputBuffer, bytesWritten);
@ -355,8 +368,6 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
} // end if bag wasn't empty, and so we sent stuff...
_myServer->unlockTree();
return truePacketsSent;
}

View file

@ -46,7 +46,11 @@ const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxe
void attachVoxelNodeDataToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
newNode->setLinkedData(new VoxelNodeData(newNode));
VoxelNodeData* voxelNodeData = new VoxelNodeData(newNode);
QUuid nodeUUID = newNode->getUUID();
qDebug("attachVoxelNodeDataToNode() newNode=%p voxelNodeData=%p\n", newNode, voxelNodeData);
qDebug() << "attachVoxelNodeDataToNode() node UUID:" << nodeUUID << "\n";
newNode->setLinkedData(voxelNodeData);
}
}
@ -171,6 +175,47 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "\r\n");
// display voxel file load time
if (GetInstance()->isInitialLoadComplete()) {
tm* voxelsLoadedAtLocal = localtime(GetInstance()->getLoadCompleted());
const int MAX_TIME_LENGTH = 128;
char buffer[MAX_TIME_LENGTH];
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtLocal);
mg_printf(connection, "Voxels Loaded At: %s", buffer);
// Convert now to tm struct for UTC
tm* voxelsLoadedAtUTM = gmtime(GetInstance()->getLoadCompleted());
if (gmtm != NULL) {
strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtUTM);
mg_printf(connection, " [%s UTM] ", buffer);
}
mg_printf(connection, "%s", "\r\n");
uint64_t msecsElapsed = GetInstance()->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", "Voxels 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", "Voxels not yet loaded...\r\n");
}
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "\r\n");
mg_printf(connection, "%s", "Configuration: \r\n ");
for (int i = 1; i < GetInstance()->_argc; i++) {
@ -339,8 +384,6 @@ void VoxelServer::run() {
parsePayload();
}
pthread_mutex_init(&_treeLock, NULL);
qInstallMessageHandler(Logging::verboseMessageHandler);
const char* STATUS_PORT = "--statusPort";
@ -510,8 +553,8 @@ void VoxelServer::run() {
// loop to send to nodes requesting data
while (true) {
if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
qDebug() << "Exit loop... getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS\n";
break;
}
@ -567,28 +610,39 @@ void VoxelServer::run() {
}
}
}
qDebug() << "VoxelServer::run()... AFTER loop...\n";
delete _jurisdiction;
// call NodeList::clear() so that all of our node specific objects, including our sending threads, are
// properly shutdown and cleaned up.
NodeList::getInstance()->clear();
qDebug() << "VoxelServer::run()... terminating _jurisdictionSender\n";
if (_jurisdictionSender) {
_jurisdictionSender->terminate();
delete _jurisdictionSender;
}
qDebug() << "VoxelServer::run()... terminating _voxelServerPacketProcessor\n";
if (_voxelServerPacketProcessor) {
_voxelServerPacketProcessor->terminate();
delete _voxelServerPacketProcessor;
}
qDebug() << "VoxelServer::run()... terminating _voxelPersistThread\n";
if (_voxelPersistThread) {
_voxelPersistThread->terminate();
delete _voxelPersistThread;
}
// tell our NodeList we're done with notifications
qDebug() << "VoxelServer::run()... nodeList->removeHook(&_nodeWatcher)\n";
nodeList->removeHook(&_nodeWatcher);
pthread_mutex_destroy(&_treeLock);
qDebug() << "VoxelServer::run()... deleting _jurisdiction\n";
delete _jurisdiction;
_jurisdiction = NULL;
qDebug() << "VoxelServer::run()... DONE\n";
}

View file

@ -48,10 +48,6 @@ public:
VoxelTree& getServerTree() { return _serverTree; }
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
void lockTree() { pthread_mutex_lock(&_treeLock); }
void unlockTree() { pthread_mutex_unlock(&_treeLock); }
VoxelTree* getTree() { return &_serverTree; }
int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; }
bool getSendMinimalEnvironment() const { return _sendMinimalEnvironment; }
EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; }
@ -59,6 +55,10 @@ public:
static VoxelServer* GetInstance() { return _theInstance; }
bool isInitialLoadComplete() const { return (_voxelPersistThread) ? _voxelPersistThread->isInitialLoadComplete() : true; }
time_t* getLoadCompleted() { return (_voxelPersistThread) ? _voxelPersistThread->getLoadCompleted() : NULL; }
uint64_t getLoadElapsedTime() const { return (_voxelPersistThread) ? _voxelPersistThread->getLoadElapsedTime() : 0; }
private:
int _argc;
const char** _argv;
@ -80,7 +80,6 @@ private:
JurisdictionSender* _jurisdictionSender;
VoxelServerPacketProcessor* _voxelServerPacketProcessor;
VoxelPersistThread* _voxelPersistThread;
pthread_mutex_t _treeLock;
EnvironmentData _environmentData[3];
NodeWatcher _nodeWatcher; // used to cleanup AGENT data when agents are killed

View file

@ -85,10 +85,10 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned
printf("inserting voxel: %f,%f,%f r=%d,g=%d,b=%d\n", vertices[0], vertices[1], vertices[2], red, green, blue);
delete[] vertices;
}
_myServer->lockTree();
_myServer->getServerTree().lockForWrite();
_myServer->getServerTree().readCodeColorBufferToTree(voxelData, destructive);
_myServer->unlockTree();
_myServer->getServerTree().unlock();
// skip to next voxel edit record in the packet
voxelData += voxelDataSize;
@ -114,9 +114,9 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned
} else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) {
// Send these bits off to the VoxelTree class to process them
_myServer->lockTree();
_myServer->getServerTree().lockForWrite();
_myServer->getServerTree().processRemoveVoxelBitstream((unsigned char*)packetData, packetLength);
_myServer->unlockTree();
_myServer->getServerTree().unlock();
// Make sure our Node and NodeList knows we've heard from this node.
Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress);

View file

@ -109,8 +109,47 @@ JurisdictionMap::JurisdictionMap(unsigned char* rootOctalCode, const std::vector
init(rootOctalCode, endNodes);
}
void myDebugoutputBits(unsigned char byte, bool withNewLine) {
if (isalnum(byte)) {
printf("[ %d (%c): ", byte, byte);
} else {
printf("[ %d (0x%x): ", byte, byte);
}
for (int i = 0; i < 8; i++) {
printf("%d", byte >> (7 - i) & 1);
}
printf(" ] ");
if (withNewLine) {
printf("\n");
}
}
void myDebugPrintOctalCode(const unsigned char* octalCode, bool withNewLine) {
if (!octalCode) {
printf("NULL");
} else {
for (int i = 0; i < bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octalCode)); i++) {
myDebugoutputBits(octalCode[i],false);
}
}
if (withNewLine) {
printf("\n");
}
}
JurisdictionMap::JurisdictionMap(const char* rootHexCode, const char* endNodesHexCodes) {
qDebug("JurisdictionMap::JurisdictionMap(const char* rootHexCode=[%p] %s, const char* endNodesHexCodes=[%p] %s)\n",
rootHexCode, rootHexCode, endNodesHexCodes, endNodesHexCodes);
_rootOctalCode = hexStringToOctalCode(QString(rootHexCode));
qDebug("JurisdictionMap::JurisdictionMap() _rootOctalCode=%p octalCode=", _rootOctalCode);
myDebugPrintOctalCode(_rootOctalCode, true);
QString endNodesHexStrings(endNodesHexCodes);
QString delimiterPattern(",");
@ -120,8 +159,16 @@ JurisdictionMap::JurisdictionMap(const char* rootHexCode, const char* endNodesHe
QString endNodeHexString = endNodeList.at(i);
unsigned char* endNodeOctcode = hexStringToOctalCode(endNodeHexString);
qDebug("JurisdictionMap::JurisdictionMap() endNodeList(%d)=%s\n",
i, endNodeHexString.toLocal8Bit().constData());
//printOctalCode(endNodeOctcode);
_endNodes.push_back(endNodeOctcode);
qDebug("JurisdictionMap::JurisdictionMap() endNodeOctcode=%p octalCode=", endNodeOctcode);
myDebugPrintOctalCode(endNodeOctcode, true);
}
}

View file

@ -42,6 +42,7 @@ VoxelQuery::VoxelQuery(Node* owningNode) :
}
VoxelQuery::~VoxelQuery() {
qDebug("VoxelQuery::~VoxelQuery()\n");
}
int VoxelQuery::getBroadcastData(unsigned char* destinationBuffer) {

View file

@ -35,7 +35,7 @@ class VoxelQuery : public NodeData {
public:
VoxelQuery(Node* owningNode = NULL);
~VoxelQuery();
virtual ~VoxelQuery();
int getBroadcastData(unsigned char* destinationBuffer);
int parseData(unsigned char* sourceBuffer, int numBytes);

View file

@ -1806,9 +1806,10 @@ void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) {
while (!nodeBag.isEmpty()) {
VoxelNode* subTree = nodeBag.extract();
lockForRead(); // do tree locking down here so that we have shorter slices and less thread contention
EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS);
bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params);
unlock();
file.write((const char*)&outputBuffer[0], bytesWritten);
}
}

View file

@ -21,6 +21,7 @@
#include "VoxelEditPacketSender.h"
#include <QObject>
#include <QReadWriteLock>
// Callback function, for recuseTreeWithOperation
typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData);
@ -185,6 +186,13 @@ public:
// reads voxels from square image with alpha as a Y-axis
bool readFromSquareARGB32Pixels(const char *filename);
bool readFromSchematicFile(const char* filename);
// VoxelTree does not currently handle its own locking, caller must use these to lock/unlock
void lockForRead() { lock.lockForRead(); }
void tryLockForRead() { lock.tryLockForRead(); }
void lockForWrite() { lock.lockForWrite(); }
void tryLockForWrite() { lock.tryLockForWrite(); }
void unlock() { lock.unlock(); }
unsigned long getVoxelCount();
@ -266,6 +274,8 @@ private:
static bool nudgeCheck(VoxelNode* node, void* extraData);
void nudgeLeaf(VoxelNode* node, void* extraData);
void chunkifyLeaf(VoxelNode* node);
QReadWriteLock lock;
};
float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale);