Merge pull request #1312 from ZappoMan/more_on_particle_server

First cut at basic Particle Server
This commit is contained in:
Philip Rosedale 2013-12-05 11:29:01 -08:00
commit 0c4b66ef54
57 changed files with 1405 additions and 951 deletions

View file

@ -159,7 +159,7 @@ static void renderMovingBug() {
}
// send the "erase message" first...
PACKET_TYPE message = PACKET_TYPE_ERASE_VOXEL;
PACKET_TYPE message = PACKET_TYPE_VOXEL_ERASE;
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details);
// Move the bug...
@ -219,7 +219,7 @@ static void renderMovingBug() {
}
// send the "create message" ...
message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE;
message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE;
::voxelEditPacketSender->queueVoxelEditMessages(message, VOXELS_PER_BUG, (VoxelDetail*)&details);
}
@ -254,7 +254,7 @@ static void sendVoxelBlinkMessage() {
detail.green = 0 * ::intensity;
detail.blue = 0 * ::intensity;
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE;
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE;
::voxelEditPacketSender->sendVoxelEditMessage(message, detail);
}
@ -271,7 +271,7 @@ unsigned char onColor[3] = { 0, 255, 255 };
const float STRING_OF_LIGHTS_SIZE = 0.125f / TREE_SCALE; // approximately 1/8th meter
static void sendBlinkingStringOfLights() {
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
float lightScale = STRING_OF_LIGHTS_SIZE;
static VoxelDetail details[LIGHTS_PER_SEGMENT];
@ -377,7 +377,7 @@ const int PACKETS_PER_DANCE_FLOOR = DANCE_FLOOR_VOXELS_PER_PACKET / (DANCE_FLOOR
int danceFloorColors[DANCE_FLOOR_WIDTH][DANCE_FLOOR_LENGTH];
void sendDanceFloor() {
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
float lightScale = DANCE_FLOOR_LIGHT_SIZE;
static VoxelDetail details[DANCE_FLOOR_VOXELS_PER_PACKET];
@ -493,7 +493,7 @@ bool billboardMessage[BILLBOARD_HEIGHT][BILLBOARD_WIDTH] = {
};
static void sendBillboard() {
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
float lightScale = BILLBOARD_LIGHT_SIZE;
static VoxelDetail details[VOXELS_PER_PACKET];
@ -564,7 +564,7 @@ void doBuildStreet() {
return;
}
PACKET_TYPE message = PACKET_TYPE_SET_VOXEL_DESTRUCTIVE; // we're a bully!
PACKET_TYPE message = PACKET_TYPE_VOXEL_SET_DESTRUCTIVE; // we're a bully!
static VoxelDetail details[BRICKS_PER_PACKET];
for (int z = 0; z < ROAD_LENGTH; z++) {
@ -864,7 +864,7 @@ int main(int argc, const char * argv[])
nodeSockAddr.getPortPointer())) &&
packetVersionMatch(packetData)) {
if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) {
if (packetData[0] == PACKET_TYPE_JURISDICTION) {
if (::jurisdictionListener) {
::jurisdictionListener->queueReceivedPacket(nodeSockAddr, packetData, receivedBytes);
}

View file

@ -26,7 +26,11 @@ link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxel-server-library ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(particle-server ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxel-server ${TARGET_NAME} ${ROOT_DIR})
#testing
include_directories(${ROOT_DIR}/externals/civetweb/include)

View file

@ -40,7 +40,7 @@ void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3) {
}
void Agent::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
if (dataByteArray[0] == PACKET_TYPE_VOXEL_JURISDICTION) {
if (dataByteArray[0] == PACKET_TYPE_JURISDICTION) {
_voxelScriptingInterface.getJurisdictionListener()->queueReceivedPacket(senderSockAddr,
(unsigned char*) dataByteArray.data(),
dataByteArray.size());

View file

@ -12,6 +12,7 @@
#include "audio/AudioMixer.h"
#include "avatars/AvatarMixer.h"
#include <VoxelServer.h>
#include <ParticleServer.h>
#include "AssignmentFactory.h"
@ -30,6 +31,8 @@ ThreadedAssignment* AssignmentFactory::unpackAssignment(const unsigned char* dat
return new Agent(dataBuffer, numBytes);
case Assignment::VoxelServerType:
return new VoxelServer(dataBuffer, numBytes);
case Assignment::ParticleServerType:
return new ParticleServer(dataBuffer, numBytes);
default:
return NULL;
}

View file

@ -22,7 +22,7 @@ void VoxelScriptingInterface::queueVoxelAdd(float x, float y, float z, float sca
VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue};
// queue the packet
queueVoxelAdd(PACKET_TYPE_SET_VOXEL, addVoxelDetail);
queueVoxelAdd(PACKET_TYPE_VOXEL_SET, addVoxelDetail);
}
void VoxelScriptingInterface::queueDestructiveVoxelAdd(float x, float y, float z, float scale,
@ -31,7 +31,7 @@ void VoxelScriptingInterface::queueDestructiveVoxelAdd(float x, float y, float z
VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue};
// queue the destructive add
queueVoxelAdd(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE, addVoxelDetail);
queueVoxelAdd(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, addVoxelDetail);
}
void VoxelScriptingInterface::queueVoxelDelete(float x, float y, float z, float scale) {
@ -39,6 +39,6 @@ void VoxelScriptingInterface::queueVoxelDelete(float x, float y, float z, float
// setup a VoxelDetail struct with data
VoxelDetail deleteVoxelDetail = {x, y, z, scale, 0, 0, 0};
_voxelPacketSender.queueVoxelEditMessages(PACKET_TYPE_ERASE_VOXEL, 1, &deleteVoxelDetail);
_voxelPacketSender.queueVoxelEditMessages(PACKET_TYPE_VOXEL_ERASE, 1, &deleteVoxelDetail);
}

View file

@ -318,6 +318,9 @@ DomainServer::DomainServer(int argc, char* argv[]) :
const char VOXEL_CONFIG_OPTION[] = "--voxelServerConfig";
_voxelServerConfig = getCmdOption(argc, (const char**) argv, VOXEL_CONFIG_OPTION);
const char PARTICLE_CONFIG_OPTION[] = "--particleServerConfig";
_particleServerConfig = getCmdOption(argc, (const char**) argv, PARTICLE_CONFIG_OPTION);
// setup the mongoose web server
struct mg_callbacks callbacks = {};
@ -390,6 +393,48 @@ void DomainServer::prepopulateStaticAssignmentFile() {
Assignment rootVoxelServerAssignment(Assignment::CreateCommand, Assignment::VoxelServerType);
freshStaticAssignments[numFreshStaticAssignments++] = rootVoxelServerAssignment;
}
// Handle Domain/Particle Server configuration command line arguments
if (_particleServerConfig) {
qDebug("Reading Particle Server Configuration.\n");
qDebug() << "config: " << _particleServerConfig << "\n";
QString multiConfig((const char*) _particleServerConfig);
QStringList multiConfigList = multiConfig.split(";");
// read each config to a payload for a VS assignment
for (int i = 0; i < multiConfigList.size(); i++) {
QString config = multiConfigList.at(i);
qDebug("config[%d]=%s\n", i, config.toLocal8Bit().constData());
// Now, parse the config to check for a pool
const char ASSIGNMENT_CONFIG_POOL_OPTION[] = "--pool";
QString assignmentPool;
int poolIndex = config.indexOf(ASSIGNMENT_CONFIG_POOL_OPTION);
if (poolIndex >= 0) {
int spaceBeforePoolIndex = config.indexOf(' ', poolIndex);
int spaceAfterPoolIndex = config.indexOf(' ', spaceBeforePoolIndex);
assignmentPool = config.mid(spaceBeforePoolIndex + 1, spaceAfterPoolIndex);
qDebug() << "The pool for this particle-assignment is" << assignmentPool << "\n";
}
Assignment particleServerAssignment(Assignment::CreateCommand,
Assignment::ParticleServerType,
(assignmentPool.isEmpty() ? NULL : assignmentPool.toLocal8Bit().constData()));
int payloadLength = config.length() + sizeof(char);
particleServerAssignment.setPayload((uchar*)config.toLocal8Bit().constData(), payloadLength);
freshStaticAssignments[numFreshStaticAssignments++] = particleServerAssignment;
}
} else {
Assignment rootParticleServerAssignment(Assignment::CreateCommand, Assignment::ParticleServerType);
freshStaticAssignments[numFreshStaticAssignments++] = rootParticleServerAssignment;
}
qDebug() << "Adding" << numFreshStaticAssignments << "static assignments to fresh file.\n";

View file

@ -64,6 +64,7 @@ private:
Assignment* _staticAssignments;
const char* _voxelServerConfig;
const char* _particleServerConfig;
bool _hasCompletedRestartHold;
};

View file

@ -1478,7 +1478,7 @@ void Application::removeVoxel(glm::vec3 position,
voxel.y = position.y / TREE_SCALE;
voxel.z = position.z / TREE_SCALE;
voxel.s = scale / TREE_SCALE;
_voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, voxel);
_voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, voxel);
// delete it locally to see the effect immediately (and in case no voxel server is present)
_voxels.deleteVoxelAt(voxel.x, voxel.y, voxel.z, voxel.s);
@ -1498,7 +1498,7 @@ void Application::makeVoxel(glm::vec3 position,
voxel.red = red;
voxel.green = green;
voxel.blue = blue;
PACKET_TYPE message = isDestructive ? PACKET_TYPE_SET_VOXEL_DESTRUCTIVE : PACKET_TYPE_SET_VOXEL;
PACKET_TYPE message = isDestructive ? PACKET_TYPE_VOXEL_SET_DESTRUCTIVE : PACKET_TYPE_VOXEL_SET;
_voxelEditSender.sendVoxelEditMessage(message, voxel);
// create the voxel locally so it appears immediately
@ -1580,7 +1580,7 @@ bool Application::sendVoxelsOperation(OctreeElement* element, void* extraData) {
codeColorBuffer[bytesInCode + RED_INDEX] = voxel->getColor()[RED_INDEX];
codeColorBuffer[bytesInCode + GREEN_INDEX] = voxel->getColor()[GREEN_INDEX];
codeColorBuffer[bytesInCode + BLUE_INDEX] = voxel->getColor()[BLUE_INDEX];
getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE,
getInstance()->_voxelEditSender.queueVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE,
codeColorBuffer, codeAndColorLength);
delete[] codeColorBuffer;
@ -3977,7 +3977,7 @@ bool Application::maybeEditVoxelUnderCursor() {
void Application::deleteVoxelUnderCursor() {
if (_mouseVoxel.s != 0) {
// sending delete to the server is sufficient, server will send new version so we see updates soon enough
_voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, _mouseVoxel);
_voxelEditSender.sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, _mouseVoxel);
// delete it locally to see the effect immediately (and in case no voxel server is present)
_voxels.deleteVoxelAt(_mouseVoxel.x, _mouseVoxel.y, _mouseVoxel.z, _mouseVoxel.s);
@ -4235,8 +4235,8 @@ void* Application::networkReceive(void* args) {
app->_audio.addReceivedAudioToBuffer(app->_incomingPacket, bytesReceived);
break;
case PACKET_TYPE_VOXEL_DATA:
case PACKET_TYPE_ERASE_VOXEL:
case PACKET_TYPE_VOXEL_STATS:
case PACKET_TYPE_VOXEL_ERASE:
case PACKET_TYPE_OCTREE_STATS:
case PACKET_TYPE_ENVIRONMENT_DATA: {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"Application::networkReceive()... _voxelProcessor.queueReceivedPacket()");

View file

@ -34,10 +34,10 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
app->_wantToKillLocalVoxels = false;
}
// note: PACKET_TYPE_VOXEL_STATS can have PACKET_TYPE_VOXEL_DATA
// immediately following them inside the same packet. So, we process the PACKET_TYPE_VOXEL_STATS first
// note: PACKET_TYPE_OCTREE_STATS can have PACKET_TYPE_VOXEL_DATA
// immediately following them inside the same packet. So, we process the PACKET_TYPE_OCTREE_STATS first
// then process any remaining bytes as if it was another packet
if (packetData[0] == PACKET_TYPE_VOXEL_STATS) {
if (packetData[0] == PACKET_TYPE_OCTREE_STATS) {
int statsMessageLength = app->parseVoxelStats(packetData, messageLength, senderSockAddr);
wasStatsPacket = true;

View file

@ -6,7 +6,7 @@ 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 voxel-server-library)
set(TARGET_NAME octree-server)
find_package(Qt5Widgets REQUIRED)
@ -36,8 +36,7 @@ link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi octree library
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi voxels library
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi avatars library
link_hifi_library(avatars ${TARGET_NAME} ${ROOT_DIR})
# link dl library on UNIX for civetweb
if (UNIX AND NOT APPLE)
target_link_libraries(${TARGET_NAME} ${CMAKE_DL_LIBS})
endif (UNIX AND NOT APPLE)

View file

@ -0,0 +1,168 @@
//
// OctreeInboundPacketProcessor.cpp
// voxel-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 voxel-server
//
#include <PacketHeaders.h>
#include <PerfStat.h>
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
#include "OctreeInboundPacketProcessor.h"
static QUuid DEFAULT_NODE_ID_REF;
OctreeInboundPacketProcessor::OctreeInboundPacketProcessor(OctreeServer* myServer) :
_myServer(myServer),
_receivedPacketCount(0),
_totalTransitTime(0),
_totalProcessTime(0),
_totalLockWaitTime(0),
_totalElementsInPacket(0),
_totalPackets(0)
{
}
void OctreeInboundPacketProcessor::resetStats() {
_totalTransitTime = 0;
_totalProcessTime = 0;
_totalLockWaitTime = 0;
_totalElementsInPacket = 0;
_totalPackets = 0;
_singleSenderStats.clear();
}
void OctreeInboundPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr,
unsigned char* packetData, ssize_t packetLength) {
bool debugProcessPacket = _myServer->wantsVerboseDebug();
if (debugProcessPacket) {
printf("OctreeInboundPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength);
}
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
// Ask our tree subclass if it can handle the incoming packet...
PACKET_TYPE packetType = packetData[0];
if (_myServer->getOctree()->handlesEditPacketType(packetType)) {
PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE",debugProcessPacket);
_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 editsInPacket = 0;
uint64_t processTime = 0;
uint64_t lockWaitTime = 0;
if (_myServer->wantsDebugReceiving()) {
printf("PROCESSING THREAD: got %c - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n",
packetType, _receivedPacketCount, packetLength, sequence, transitTime);
}
int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt);
unsigned char* editData = (unsigned char*)&packetData[atByte];
while (atByte < packetLength) {
int maxSize = packetLength - atByte;
if (debugProcessPacket) {
printf("OctreeInboundPacketProcessor::processPacket() %c "
"packetData=%p packetLength=%ld voxelData=%p atByte=%d maxSize=%d\n",
packetType, packetData, packetLength, editData, atByte, maxSize);
}
uint64_t startLock = usecTimestampNow();
_myServer->getOctree()->lockForWrite();
uint64_t startProcess = usecTimestampNow();
int editDataBytesRead = _myServer->getOctree()->processEditPacketData(packetType,
packetData, packetLength, editData, maxSize);
_myServer->getOctree()->unlock();
uint64_t endProcess = usecTimestampNow();
editsInPacket++;
uint64_t thisProcessTime = endProcess - startProcess;
uint64_t thisLockWaitTime = startProcess - startLock;
processTime += thisProcessTime;
lockWaitTime += thisLockWaitTime;
// skip to next voxel edit record in the packet
editData += editDataBytesRead;
atByte += editDataBytesRead;
}
if (debugProcessPacket) {
printf("OctreeInboundPacketProcessor::processPacket() DONE LOOPING FOR %c "
"packetData=%p packetLength=%ld voxelData=%p atByte=%d\n",
packetType, packetData, packetLength, editData, atByte);
}
// Make sure our Node and NodeList knows we've heard from this node.
Node* senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
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, editsInPacket, processTime, lockWaitTime);
} else {
printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]);
}
}
void OctreeInboundPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
int editsInPacket, uint64_t processTime, uint64_t lockWaitTime) {
_totalTransitTime += transitTime;
_totalProcessTime += processTime;
_totalLockWaitTime += lockWaitTime;
_totalElementsInPacket += editsInPacket;
_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._totalElementsInPacket += editsInPacket;
stats._totalPackets++;
_singleSenderStats[nodeUUID] = stats;
} else {
SingleSenderStats& stats = _singleSenderStats[nodeUUID];
stats._totalTransitTime += transitTime;
stats._totalProcessTime += processTime;
stats._totalLockWaitTime += lockWaitTime;
stats._totalElementsInPacket += editsInPacket;
stats._totalPackets++;
}
}
SingleSenderStats::SingleSenderStats() {
_totalTransitTime = 0;
_totalProcessTime = 0;
_totalLockWaitTime = 0;
_totalElementsInPacket = 0;
_totalPackets = 0;
}

View file

@ -1,5 +1,5 @@
//
// VoxelServerPacketProcessor.h
// OctreeInboundPacketProcessor.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
@ -8,13 +8,13 @@
// Threaded or non-threaded network packet processor for the voxel-server
//
#ifndef __voxel_server__VoxelServerPacketProcessor__
#define __voxel_server__VoxelServerPacketProcessor__
#ifndef __octree_server__OctreeInboundPacketProcessor__
#define __octree_server__OctreeInboundPacketProcessor__
#include <map>
#include <ReceivedPacketProcessor.h>
class VoxelServer;
class OctreeServer;
class SingleSenderStats {
public:
@ -23,17 +23,17 @@ public:
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 getTotalVoxelsProcessed() const { return _totalVoxelsInPacket; }
uint64_t getTotalElementsProcessed() const { return _totalElementsInPacket; }
uint64_t getTotalPacketsProcessed() const { return _totalPackets; }
uint64_t getAverageProcessTimePerVoxel() const
{ return _totalVoxelsInPacket == 0 ? 0 : _totalProcessTime / _totalVoxelsInPacket; }
uint64_t getAverageLockWaitTimePerVoxel() const
{ return _totalVoxelsInPacket == 0 ? 0 : _totalLockWaitTime / _totalVoxelsInPacket; }
uint64_t getAverageProcessTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; }
uint64_t getAverageLockWaitTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; }
uint64_t _totalTransitTime;
uint64_t _totalProcessTime;
uint64_t _totalLockWaitTime;
uint64_t _totalVoxelsInPacket;
uint64_t _totalElementsInPacket;
uint64_t _totalPackets;
};
@ -43,20 +43,20 @@ typedef std::map<QUuid, SingleSenderStats>::iterator NodeToSenderStatsMapIterato
/// Handles processing of incoming network packets for the voxel-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 VoxelServerPacketProcessor : public ReceivedPacketProcessor {
class OctreeInboundPacketProcessor : public ReceivedPacketProcessor {
public:
VoxelServerPacketProcessor(VoxelServer* myServer);
OctreeInboundPacketProcessor(OctreeServer* 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 getTotalVoxelsProcessed() const { return _totalVoxelsInPacket; }
uint64_t getTotalElementsProcessed() const { return _totalElementsInPacket; }
uint64_t getTotalPacketsProcessed() const { return _totalPackets; }
uint64_t getAverageProcessTimePerVoxel() const
{ return _totalVoxelsInPacket == 0 ? 0 : _totalProcessTime / _totalVoxelsInPacket; }
uint64_t getAverageLockWaitTimePerVoxel() const
{ return _totalVoxelsInPacket == 0 ? 0 : _totalLockWaitTime / _totalVoxelsInPacket; }
uint64_t getAverageProcessTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalProcessTime / _totalElementsInPacket; }
uint64_t getAverageLockWaitTimePerElement() const
{ return _totalElementsInPacket == 0 ? 0 : _totalLockWaitTime / _totalElementsInPacket; }
void resetStats();
@ -69,15 +69,15 @@ private:
void trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
int voxelsInPacket, uint64_t processTime, uint64_t lockWaitTime);
VoxelServer* _myServer;
OctreeServer* _myServer;
int _receivedPacketCount;
uint64_t _totalTransitTime;
uint64_t _totalProcessTime;
uint64_t _totalLockWaitTime;
uint64_t _totalVoxelsInPacket;
uint64_t _totalElementsInPacket;
uint64_t _totalPackets;
NodeToSenderStatsMap _singleSenderStats;
};
#endif // __voxel_server__VoxelServerPacketProcessor__
#endif // __octree_server__OctreeInboundPacketProcessor__

View file

@ -1,22 +1,21 @@
//
// VoxelPersistThread.cpp
// voxel-server
// OctreePersistThread.cpp
// Octree-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded voxel persistence
// Threaded or non-threaded Octree persistence
//
#include <QDebug>
#include <NodeList.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include "VoxelPersistThread.h"
#include "VoxelServer.h"
#include "OctreePersistThread.h"
#include "OctreeServer.h"
VoxelPersistThread::VoxelPersistThread(VoxelTree* tree, const char* filename, int persistInterval) :
OctreePersistThread::OctreePersistThread(Octree* tree, const char* filename, int persistInterval) :
_tree(tree),
_filename(filename),
_persistInterval(persistInterval),
@ -24,17 +23,17 @@ VoxelPersistThread::VoxelPersistThread(VoxelTree* tree, const char* filename, in
_loadTimeUSecs(0) {
}
bool VoxelPersistThread::process() {
bool OctreePersistThread::process() {
if (!_initialLoadComplete) {
uint64_t loadStarted = usecTimestampNow();
qDebug("loading voxels from file: %s...\n", _filename);
qDebug("loading Octrees from file: %s...\n", _filename);
bool persistantFileRead;
_tree->lockForWrite();
{
PerformanceWarning warn(true, "Loading Voxel File", true);
PerformanceWarning warn(true, "Loading Octree File", true);
persistantFileRead = _tree->readFromSVOFile(_filename);
}
_tree->unlock();
@ -44,7 +43,7 @@ bool VoxelPersistThread::process() {
_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));
qDebug("DONE loading Octrees from file... fileRead=%s\n", debug::valueOf(persistantFileRead));
unsigned long nodeCount = OctreeElement::getNodeCount();
unsigned long internalNodeCount = OctreeElement::getInternalNodeCount();
@ -75,10 +74,10 @@ bool VoxelPersistThread::process() {
// check the dirty bit and persist here...
_lastCheck = usecTimestampNow();
if (_tree->isDirty()) {
qDebug("saving voxels to file %s...\n",_filename);
qDebug("saving Octrees to file %s...\n",_filename);
_tree->writeToSVOFile(_filename);
_tree->clearDirtyBit(); // tree is clean after saving
qDebug("DONE saving voxels to file...\n");
qDebug("DONE saving Octrees to file...\n");
}
}
}

View file

@ -1,26 +1,25 @@
//
// VoxelPersistThread.h
// voxel-server
// OctreePersistThread.h
// Octree-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded voxel persistence
// Threaded or non-threaded Octree persistence
//
#ifndef __voxel_server__VoxelPersistThread__
#define __voxel_server__VoxelPersistThread__
#ifndef __Octree_server__OctreePersistThread__
#define __Octree_server__OctreePersistThread__
#include <GenericThread.h>
#include <NetworkPacket.h>
#include <VoxelTree.h>
#include <Octree.h>
/// Generalized threaded processor for handling received inbound packets.
class VoxelPersistThread : public virtual GenericThread {
class OctreePersistThread : public virtual GenericThread {
public:
static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds
VoxelPersistThread(VoxelTree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL);
OctreePersistThread(Octree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL);
bool isInitialLoadComplete() const { return _initialLoadComplete; }
@ -31,7 +30,7 @@ protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
private:
VoxelTree* _tree;
Octree* _tree;
const char* _filename;
int _persistInterval;
bool _initialLoadComplete;
@ -41,4 +40,4 @@ private:
uint64_t _lastCheck;
};
#endif // __voxel_server__VoxelPersistThread__
#endif // __Octree_server__OctreePersistThread__

View file

@ -1,5 +1,5 @@
//
// VoxelNodeData.cpp
// OctreeQueryNode.cpp
// hifi
//
// Created by Stephen Birarda on 3/21/13.
@ -8,15 +8,15 @@
#include "PacketHeaders.h"
#include "SharedUtil.h"
#include "VoxelNodeData.h"
#include "OctreeQueryNode.h"
#include <cstring>
#include <cstdio>
#include "VoxelSendThread.h"
#include "OctreeSendThread.h"
VoxelNodeData::VoxelNodeData(Node* owningNode) :
VoxelQuery(owningNode),
OctreeQueryNode::OctreeQueryNode(Node* owningNode) :
OctreeQuery(owningNode),
_viewSent(false),
_voxelPacketAvailableBytes(MAX_PACKET_SIZE),
_octreePacketAvailableBytes(MAX_PACKET_SIZE),
_maxSearchLevel(1),
_maxLevelReachedInLastSearch(1),
_lastTimeBagEmpty(0),
@ -24,39 +24,38 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
_viewFrustumJustStoppedChanging(true),
_currentPacketIsColor(true),
_currentPacketIsCompressed(false),
_voxelSendThread(NULL),
_octreeSendThread(NULL),
_lastClientBoundaryLevelAdjust(0),
_lastClientVoxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
_lastClientOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
_lodChanged(false),
_lodInitialized(false)
{
_voxelPacket = new unsigned char[MAX_PACKET_SIZE];
_voxelPacketAt = _voxelPacket;
_lastVoxelPacket = new unsigned char[MAX_PACKET_SIZE];
_lastVoxelPacketLength = 0;
_octreePacket = new unsigned char[MAX_PACKET_SIZE];
_octreePacketAt = _octreePacket;
_lastOctreePacket = new unsigned char[MAX_PACKET_SIZE];
_lastOctreePacketLength = 0;
_duplicatePacketCount = 0;
_sequenceNumber = 0;
resetVoxelPacket(true); // don't bump sequence
}
void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) {
// Create voxel sending thread...
void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer) {
// Create octree sending thread...
QUuid nodeUUID = getOwningNode()->getUUID();
_voxelSendThread = new VoxelSendThread(nodeUUID, voxelServer);
_voxelSendThread->initialize(true);
_octreeSendThread = new OctreeSendThread(nodeUUID, octreeServer);
_octreeSendThread->initialize(true);
}
bool VoxelNodeData::packetIsDuplicate() const {
bool OctreeQueryNode::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 (_lastVoxelPacketLength == getPacketLength()) {
return memcmp(_lastVoxelPacket + VOXEL_PACKET_HEADER_SIZE,
_voxelPacket+VOXEL_PACKET_HEADER_SIZE , getPacketLength() - VOXEL_PACKET_HEADER_SIZE) == 0;
if (_lastOctreePacketLength == getPacketLength()) {
return memcmp(_lastOctreePacket + OCTREE_PACKET_HEADER_SIZE,
_octreePacket+OCTREE_PACKET_HEADER_SIZE , getPacketLength() - OCTREE_PACKET_HEADER_SIZE == 0);
}
return false;
}
bool VoxelNodeData::shouldSuppressDuplicatePacket() {
bool OctreeQueryNode::shouldSuppressDuplicatePacket() {
bool shouldSuppress = false; // assume we won't suppress
// only consider duplicate packets
@ -90,19 +89,19 @@ bool VoxelNodeData::shouldSuppressDuplicatePacket() {
return shouldSuppress;
}
void VoxelNodeData::resetVoxelPacket(bool lastWasSurpressed) {
void OctreeQueryNode::resetOctreePacket(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.
_lastVoxelPacketLength = getPacketLength();
memcpy(_lastVoxelPacket, _voxelPacket, _lastVoxelPacketLength);
_lastOctreePacketLength = getPacketLength();
memcpy(_lastOctreePacket, _octreePacket, _lastOctreePacketLength);
// 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();
VOXEL_PACKET_FLAGS flags = 0;
OCTREE_PACKET_FLAGS flags = 0;
if (_currentPacketIsColor) {
setAtBit(flags,PACKET_IS_COLOR_BIT);
}
@ -110,63 +109,63 @@ void VoxelNodeData::resetVoxelPacket(bool lastWasSurpressed) {
setAtBit(flags,PACKET_IS_COMPRESSED_BIT);
}
_voxelPacketAvailableBytes = MAX_PACKET_SIZE;
int numBytesPacketHeader = populateTypeAndVersion(_voxelPacket, PACKET_TYPE_VOXEL_DATA);
_voxelPacketAt = _voxelPacket + numBytesPacketHeader;
_voxelPacketAvailableBytes -= numBytesPacketHeader;
_octreePacketAvailableBytes = MAX_PACKET_SIZE;
int numBytesPacketHeader = populateTypeAndVersion(_octreePacket, getMyPacketType());
_octreePacketAt = _octreePacket + numBytesPacketHeader;
_octreePacketAvailableBytes -= numBytesPacketHeader;
// pack in flags
VOXEL_PACKET_FLAGS* flagsAt = (VOXEL_PACKET_FLAGS*)_voxelPacketAt;
OCTREE_PACKET_FLAGS* flagsAt = (OCTREE_PACKET_FLAGS*)_octreePacketAt;
*flagsAt = flags;
_voxelPacketAt += sizeof(VOXEL_PACKET_FLAGS);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_FLAGS);
_octreePacketAt += sizeof(OCTREE_PACKET_FLAGS);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_FLAGS);
// pack in sequence number
VOXEL_PACKET_SEQUENCE* sequenceAt = (VOXEL_PACKET_SEQUENCE*)_voxelPacketAt;
OCTREE_PACKET_SEQUENCE* sequenceAt = (OCTREE_PACKET_SEQUENCE*)_octreePacketAt;
*sequenceAt = _sequenceNumber;
_voxelPacketAt += sizeof(VOXEL_PACKET_SEQUENCE);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_SEQUENCE);
if (!(lastWasSurpressed || _lastVoxelPacketLength == VOXEL_PACKET_HEADER_SIZE)) {
_octreePacketAt += sizeof(OCTREE_PACKET_SEQUENCE);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SEQUENCE);
if (!(lastWasSurpressed || _lastOctreePacketLength == OCTREE_PACKET_HEADER_SIZE)) {
_sequenceNumber++;
}
// pack in timestamp
VOXEL_PACKET_SENT_TIME now = usecTimestampNow();
VOXEL_PACKET_SENT_TIME* timeAt = (VOXEL_PACKET_SENT_TIME*)_voxelPacketAt;
OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
OCTREE_PACKET_SENT_TIME* timeAt = (OCTREE_PACKET_SENT_TIME*)_octreePacketAt;
*timeAt = now;
_voxelPacketAt += sizeof(VOXEL_PACKET_SENT_TIME);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_SENT_TIME);
_octreePacketAt += sizeof(OCTREE_PACKET_SENT_TIME);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SENT_TIME);
_voxelPacketWaiting = false;
_octreePacketWaiting = false;
}
void VoxelNodeData::writeToPacket(const unsigned char* buffer, int bytes) {
void OctreeQueryNode::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) {
*(VOXEL_PACKET_INTERNAL_SECTION_SIZE*)_voxelPacketAt = bytes;
_voxelPacketAt += sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
_voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
*(OCTREE_PACKET_INTERNAL_SECTION_SIZE*)_octreePacketAt = bytes;
_octreePacketAt += sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
}
if (bytes <= _voxelPacketAvailableBytes) {
memcpy(_voxelPacketAt, buffer, bytes);
_voxelPacketAvailableBytes -= bytes;
_voxelPacketAt += bytes;
_voxelPacketWaiting = true;
if (bytes <= _octreePacketAvailableBytes) {
memcpy(_octreePacketAt, buffer, bytes);
_octreePacketAvailableBytes -= bytes;
_octreePacketAt += bytes;
_octreePacketWaiting = true;
}
}
VoxelNodeData::~VoxelNodeData() {
delete[] _voxelPacket;
delete[] _lastVoxelPacket;
OctreeQueryNode::~OctreeQueryNode() {
delete[] _octreePacket;
delete[] _lastOctreePacket;
if (_voxelSendThread) {
_voxelSendThread->terminate();
delete _voxelSendThread;
if (_octreeSendThread) {
_octreeSendThread->terminate();
delete _octreeSendThread;
}
}
bool VoxelNodeData::updateCurrentViewFrustum() {
bool OctreeQueryNode::updateCurrentViewFrustum() {
bool currentViewFrustumChanged = false;
ViewFrustum newestViewFrustum;
// get position and orientation details from the camera
@ -196,13 +195,13 @@ bool VoxelNodeData::updateCurrentViewFrustum() {
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
_lodChanged = true;
}
if (_lastClientVoxelSizeScale != getOctreeSizeScale()) {
_lastClientVoxelSizeScale = getOctreeSizeScale();
if (_lastClientOctreeSizeScale != getOctreeSizeScale()) {
_lastClientOctreeSizeScale = getOctreeSizeScale();
_lodChanged = true;
}
} else {
_lodInitialized = true;
_lastClientVoxelSizeScale = getOctreeSizeScale();
_lastClientOctreeSizeScale = getOctreeSizeScale();
_lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
_lodChanged = false;
}
@ -217,7 +216,7 @@ bool VoxelNodeData::updateCurrentViewFrustum() {
return currentViewFrustumChanged;
}
void VoxelNodeData::setViewSent(bool viewSent) {
void OctreeQueryNode::setViewSent(bool viewSent) {
_viewSent = viewSent;
if (viewSent) {
_viewFrustumJustStoppedChanging = false;
@ -225,7 +224,7 @@ void VoxelNodeData::setViewSent(bool viewSent) {
}
}
void VoxelNodeData::updateLastKnownViewFrustum() {
void OctreeQueryNode::updateLastKnownViewFrustum() {
bool frustumChanges = !_lastKnownViewFrustum.isVerySimilar(_currentViewFrustum);
if (frustumChanges) {
@ -239,7 +238,7 @@ void VoxelNodeData::updateLastKnownViewFrustum() {
}
bool VoxelNodeData::moveShouldDump() const {
bool OctreeQueryNode::moveShouldDump() const {
glm::vec3 oldPosition = _lastKnownViewFrustum.getPosition();
glm::vec3 newPosition = _currentViewFrustum.getPosition();
@ -251,7 +250,7 @@ bool VoxelNodeData::moveShouldDump() const {
return false;
}
void VoxelNodeData::dumpOutOfView() {
void OctreeQueryNode::dumpOutOfView() {
int stillInView = 0;
int outOfView = 0;
OctreeElementBag tempBag;

View file

@ -1,44 +1,46 @@
//
// VoxelNodeData.h
// OctreeQueryNode.h
// hifi
//
// Created by Stephen Birarda on 3/21/13.
// Created by Brad Hefta-Gaub on 12/4/13.
//
//
#ifndef __hifi__VoxelNodeData__
#define __hifi__VoxelNodeData__
#ifndef __hifi__OctreeQueryNode__
#define __hifi__OctreeQueryNode__
#include <iostream>
#include <NodeData.h>
#include <VoxelPacketData.h>
#include <VoxelQuery.h>
#include <OctreePacketData.h>
#include <OctreeQuery.h>
#include <CoverageMap.h>
#include <VoxelConstants.h>
#include <OctreeConstants.h>
#include <OctreeElementBag.h>
#include <VoxelSceneStats.h>
#include <OctreeSceneStats.h>
class VoxelSendThread;
class VoxelServer;
class OctreeSendThread;
class OctreeServer;
class VoxelNodeData : public VoxelQuery {
class OctreeQueryNode : public OctreeQuery {
public:
VoxelNodeData(Node* owningNode);
virtual ~VoxelNodeData();
OctreeQueryNode(Node* owningNode);
virtual ~OctreeQueryNode();
virtual PACKET_TYPE getMyPacketType() const = 0;
void resetVoxelPacket(bool lastWasSurpressed = false); // resets voxel packet to after "V" header
void resetOctreePacket(bool lastWasSurpressed = false); // resets octree packet to after "V" header
void writeToPacket(const unsigned char* buffer, int bytes); // writes to end of packet
const unsigned char* getPacket() const { return _voxelPacket; }
int getPacketLength() const { return (MAX_PACKET_SIZE - _voxelPacketAvailableBytes); }
bool isPacketWaiting() const { return _voxelPacketWaiting; }
const unsigned char* getPacket() const { return _octreePacket; }
int getPacketLength() const { return (MAX_PACKET_SIZE - _octreePacketAvailableBytes); }
bool isPacketWaiting() const { return _octreePacketWaiting; }
bool packetIsDuplicate() const;
bool shouldSuppressDuplicatePacket();
int getAvailable() const { return _voxelPacketAvailableBytes; }
int getAvailable() const { return _octreePacketAvailableBytes; }
int getMaxSearchLevel() const { return _maxSearchLevel; }
void resetMaxSearchLevel() { _maxSearchLevel = 1; }
void incrementMaxSearchLevel() { _maxSearchLevel++; }
@ -76,25 +78,25 @@ public:
bool hasLodChanged() const { return _lodChanged; };
VoxelSceneStats stats;
OctreeSceneStats stats;
void initializeVoxelSendThread(VoxelServer* voxelServer);
bool isVoxelSendThreadInitalized() { return _voxelSendThread; }
void initializeOctreeSendThread(OctreeServer* octreeServer);
bool isOctreeSendThreadInitalized() { return _octreeSendThread; }
void dumpOutOfView();
private:
VoxelNodeData(const VoxelNodeData &);
VoxelNodeData& operator= (const VoxelNodeData&);
OctreeQueryNode(const OctreeQueryNode &);
OctreeQueryNode& operator= (const OctreeQueryNode&);
bool _viewSent;
unsigned char* _voxelPacket;
unsigned char* _voxelPacketAt;
int _voxelPacketAvailableBytes;
bool _voxelPacketWaiting;
unsigned char* _octreePacket;
unsigned char* _octreePacketAt;
int _octreePacketAvailableBytes;
bool _octreePacketWaiting;
unsigned char* _lastVoxelPacket;
int _lastVoxelPacketLength;
unsigned char* _lastOctreePacket;
int _lastOctreePacketLength;
int _duplicatePacketCount;
uint64_t _firstSuppressedPacket;
@ -108,15 +110,15 @@ private:
bool _currentPacketIsColor;
bool _currentPacketIsCompressed;
VoxelSendThread* _voxelSendThread;
OctreeSendThread* _octreeSendThread;
// watch for LOD changes
int _lastClientBoundaryLevelAdjust;
float _lastClientVoxelSizeScale;
float _lastClientOctreeSizeScale;
bool _lodChanged;
bool _lodInitialized;
VOXEL_PACKET_SEQUENCE _sequenceNumber;
OCTREE_PACKET_SEQUENCE _sequenceNumber;
};
#endif /* defined(__hifi__VoxelNodeData__) */
#endif /* defined(__hifi__OctreeQueryNode__) */

View file

@ -1,43 +1,34 @@
//
// VoxelSendThread.cpp
// voxel-server
// OctreeSendThread.cpp
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded voxel packet sender
//
#include <EnvironmentData.h>
#include <NodeList.h>
#include <PacketHeaders.h>
#include <PerfStat.h>
#include <SharedUtil.h>
extern EnvironmentData environmentData[3];
#include "VoxelSendThread.h"
#include "VoxelServer.h"
#include "VoxelServerConsts.h"
#include "OctreeSendThread.h"
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
uint64_t startSceneSleepTime = 0;
uint64_t endSceneSleepTime = 0;
VoxelSendThread::VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer) :
OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer) :
_nodeUUID(nodeUUID),
_myServer(myServer),
_packetData()
{
}
bool VoxelSendThread::process() {
bool OctreeSendThread::process() {
uint64_t start = usecTimestampNow();
bool gotLock = false;
// don't do any send processing until the initial load of the voxels is complete...
// don't do any send processing until the initial load of the octree is complete...
if (_myServer->isInitialLoadComplete()) {
Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID);
@ -45,41 +36,41 @@ bool VoxelSendThread::process() {
// make sure the node list doesn't kill our node while we're using it
if (node->trylock()) {
gotLock = true;
VoxelNodeData* nodeData = NULL;
OctreeQueryNode* nodeData = NULL;
nodeData = (VoxelNodeData*) node->getLinkedData();
nodeData = (OctreeQueryNode*) 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() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged));
}
packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged);
packetsSent = packetDistributor(node, nodeData, viewFrustumChanged);
}
node->unlock(); // we're done with this node for now.
}
}
} else {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
qDebug("VoxelSendThread::process() waiting for isInitialLoadComplete()\n");
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
qDebug("OctreeSendThread::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 voxels
// dynamically sleep until we need to fire off the next set of octree elements
int elapsed = (usecTimestampNow() - start);
int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed;
int usecToSleep = OCTREE_SEND_INTERVAL_USECS - elapsed;
if (usecToSleep > 0) {
PerformanceWarning warn(false,"VoxelSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
PerformanceWarning warn(false,"OctreeSendThread... usleep()",false,&_usleepTime,&_usleepCalls);
usleep(usecToSleep);
} else {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
std::cout << "Last send took too much time, not sleeping!\n";
}
}
@ -88,15 +79,15 @@ bool VoxelSendThread::process() {
return isStillRunning(); // keep running till they terminate us
}
uint64_t VoxelSendThread::_usleepTime = 0;
uint64_t VoxelSendThread::_usleepCalls = 0;
uint64_t OctreeSendThread::_usleepTime = 0;
uint64_t OctreeSendThread::_usleepCalls = 0;
uint64_t VoxelSendThread::_totalBytes = 0;
uint64_t VoxelSendThread::_totalWastedBytes = 0;
uint64_t VoxelSendThread::_totalPackets = 0;
uint64_t OctreeSendThread::_totalBytes = 0;
uint64_t OctreeSendThread::_totalWastedBytes = 0;
uint64_t OctreeSendThread::_totalPackets = 0;
int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent) {
bool debug = _myServer->wantsDebugVoxelSending();
int OctreeSendThread::handlePacketSend(Node* node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) {
bool debug = _myServer->wantsDebugSending();
uint64_t now = usecTimestampNow();
bool packetSent = false; // did we send a packet?
@ -105,16 +96,16 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
// 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->resetVoxelPacket(true); // we still need to reset it though!
nodeData->resetOctreePacket(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);
dataAt += sizeof(OCTREE_PACKET_FLAGS);
OCTREE_PACKET_SEQUENCE sequence = (*(OCTREE_PACKET_SEQUENCE*)dataAt);
dataAt += sizeof(OCTREE_PACKET_SEQUENCE);
// If we've got a stats message ready to send, then see if we can piggyback them together
@ -220,14 +211,14 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
trueBytesSent += nodeData->getPacketLength();
truePacketsSent++;
packetsSent++;
nodeData->resetVoxelPacket();
nodeData->resetOctreePacket();
}
return packetsSent;
}
/// Version of voxel distributor that sends the deepest LOD level at once
int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) {
int OctreeSendThread::packetDistributor(Node* node, OctreeQueryNode* nodeData, bool viewFrustumChanged) {
int truePacketsSent = 0;
int trueBytesSent = 0;
@ -248,7 +239,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// then let's just send that waiting packet.
if (!nodeData->getCurrentPacketFormatMatches()) {
if (nodeData->isPacketWaiting()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _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()),
@ -256,19 +247,19 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
}
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
} else {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _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->resetVoxelPacket();
nodeData->resetOctreePacket();
}
int targetSize = MAX_VOXEL_PACKET_DATA_SIZE;
int targetSize = MAX_OCTREE_PACKET_DATA_SIZE;
if (wantCompression) {
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE);
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__,
debug::valueOf(wantCompression), targetSize);
}
@ -276,7 +267,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
_packetData.changeSettings(wantCompression, targetSize);
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _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()),
@ -285,8 +276,8 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL;
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("packetDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()),
debug::valueOf(nodeData->getViewSent())
);
@ -296,7 +287,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// the current view frustum for things to send.
if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) {
uint64_t now = usecTimestampNow();
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...\n",
debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()));
if (nodeData->getLastTimeBagEmpty() > 0) {
@ -314,7 +305,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// if our view has changed, we need to reset these things...
if (viewFrustumChanged) {
if (_myServer->wantDumpVoxelsOnMove() || nodeData->moveShouldDump() || nodeData->hasLodChanged()) {
if (nodeData->moveShouldDump() || nodeData->hasLodChanged()) {
nodeData->dumpOutOfView();
}
nodeData->map.erase();
@ -335,15 +326,11 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
unsigned long elapsedTime = nodeData->stats.getElapsedTime();
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugSending()) {
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->wantDisplayVoxelStats()) {
nodeData->stats.printDebugDetails();
}
// start tracking our stats
bool isFullScene = ((!viewFrustumChanged || !nodeData->getWantDelta())
&& nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged();
@ -353,22 +340,22 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
nodeData->nodeBag.deleteAll();
}
if (_myServer->wantsDebugVoxelSending()) {
if (_myServer->wantsDebugSending()) {
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().getRoot(), _myServer->getJurisdiction());
nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot(), _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().getRoot()); // only in case of empty
nodeData->nodeBag.insert(_myServer->getOctree()->getRoot()); // only in case of empty
}
} else {
nodeData->nodeBag.insert(_myServer->getServerTree().getRoot()); // original behavior, reset on move or empty
nodeData->nodeBag.insert(_myServer->getOctree()->getRoot()); // original behavior, reset on move or empty
}
}
@ -376,23 +363,21 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
if (!nodeData->nodeBag.isEmpty()) {
int bytesWritten = 0;
uint64_t start = usecTimestampNow();
uint64_t startCompressTimeMsecs = VoxelPacketData::getCompressContentTime() / 1000;
uint64_t startCompressCalls = VoxelPacketData::getCompressContentCalls();
bool shouldSendEnvironments = _myServer->wantSendEnvironments() && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, VOXEL_SEND_INTERVAL_USECS);
uint64_t startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000;
uint64_t startCompressCalls = OctreePacketData::getCompressContentCalls();
int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND));
int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval());
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval);
}
int extraPackingAttempts = 0;
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval - (shouldSendEnvironments ? 1 : 0)) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval);
@ -421,12 +406,12 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
isFullScene, &nodeData->stats, _myServer->getJurisdiction());
_myServer->getServerTree().lockForRead();
_myServer->getOctree()->lockForRead();
nodeData->stats.encodeStarted();
bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params);
bytesWritten = _myServer->getOctree()->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.getTargetSize() == MAX_OCTREE_PACKET_DATA_SIZE) {
if (_packetData.hasContent() && bytesWritten == 0 &&
params.stopReason == EncodeBitstreamParams::DIDNT_FIT) {
lastNodeDidntFit = true;
@ -442,7 +427,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
}
nodeData->stats.encodeStopped();
_myServer->getServerTree().unlock();
_myServer->getOctree()->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;
@ -461,18 +446,18 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// 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);
+ (nodeData->getCurrentPacketIsCompressed() ? sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) : 0);
if (writtenSize > nodeData->getAvailable()) {
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _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->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n",
nodeData->getAvailable(), _packetData.getFinalizedSize(),
_packetData.getUncompressedSize(), _packetData.getTargetSize());
@ -491,11 +476,11 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
sendNow = false; // try to pack more
}
int targetSize = MAX_VOXEL_PACKET_DATA_SIZE;
int targetSize = MAX_OCTREE_PACKET_DATA_SIZE;
if (sendNow) {
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);
if (wantCompression) {
targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE);
targetSize = nodeData->getAvailable() - sizeof(OCTREE_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.
@ -504,9 +489,9 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
// 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;
targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING;
}
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__,
debug::valueOf(nodeData->getWantCompression()), targetSize);
}
@ -514,8 +499,15 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
}
}
// Here's where we can/should allow the server to send other data...
// send the environment packet
if (shouldSendEnvironments) {
if (_myServer->hasSpecialPacketToSend()) {
trueBytesSent += _myServer->sendSpecialPacket(node);
truePacketsSent++;
packetsSentThisInterval++;
/**
int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
int envPacketLength = numBytesPacketHeader;
int environmentsToSend = _myServer->getSendMinimalEnvironment() ? 1 : _myServer->getEnvironmentDataCount();
@ -530,15 +522,17 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
trueBytesSent += envPacketLength;
truePacketsSent++;
packetsSentThisInterval++;
**/
}
uint64_t end = usecTimestampNow();
int elapsedmsec = (end - start)/1000;
uint64_t endCompressCalls = VoxelPacketData::getCompressContentCalls();
uint64_t endCompressCalls = OctreePacketData::getCompressContentCalls();
int elapsedCompressCalls = endCompressCalls - startCompressCalls;
uint64_t endCompressTimeMsecs = VoxelPacketData::getCompressContentTime() / 1000;
uint64_t endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000;
int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs;
@ -551,7 +545,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
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->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
} else if (_myServer->wantsDebugSending() && _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());
}
@ -561,13 +555,13 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
if (nodeData->nodeBag.isEmpty()) {
nodeData->updateLastKnownViewFrustum();
nodeData->setViewSent(true);
if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _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->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) {
if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) {
printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n",
truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(),
nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval);

View file

@ -0,0 +1,45 @@
//
// OctreeSendThread.h
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded object for sending voxels to a client
//
#ifndef __octree_server__OctreeSendThread__
#define __octree_server__OctreeSendThread__
#include <GenericThread.h>
#include <NetworkPacket.h>
#include <OctreeElementBag.h>
#include "OctreeQueryNode.h"
#include "OctreeServer.h"
/// Threaded processor for sending voxel packets to a single client
class OctreeSendThread : public virtual GenericThread {
public:
OctreeSendThread(const QUuid& nodeUUID, OctreeServer* 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;
OctreeServer* _myServer;
int handlePacketSend(Node* node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent);
int packetDistributor(Node* node, OctreeQueryNode* nodeData, bool viewFrustumChanged);
OctreePacketData _packetData;
};
#endif // __octree_server__OctreeSendThread__

View file

@ -1,81 +1,65 @@
//
// VoxelServer.cpp
// OctreeServer.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 9/16/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <time.h>
#include <QtCore/QDebug>
#include <QtCore/QString>
#include <QtCore/QTimer>
#include <QtCore/QUuid>
#include <Logging.h>
#include <OctalCode.h>
#include <NodeList.h>
#include <NodeTypes.h>
#include <VoxelTree.h>
#include "VoxelNodeData.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 "civetweb.h"
#include "VoxelServer.h"
#include "VoxelServerConsts.h"
#include "OctreeServer.h"
#include "OctreeServerConsts.h"
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.svo";
void attachVoxelNodeDataToNode(Node* newNode) {
void OctreeServer::attachQueryNodeToNode(Node* newNode) {
if (newNode->getLinkedData() == NULL) {
VoxelNodeData* voxelNodeData = new VoxelNodeData(newNode);
newNode->setLinkedData(voxelNodeData);
if (GetInstance()) {
OctreeQueryNode* newQueryNodeData = GetInstance()->createOctreeQueryNode(newNode);
newQueryNodeData->resetOctreePacket(true); // don't bump sequence
newNode->setLinkedData(newQueryNodeData);
}
}
}
VoxelServer* VoxelServer::_theInstance = NULL;
void OctreeServer::nodeAdded(Node* node) {
// do nothing
}
VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes),
_serverTree(true)
void OctreeServer::nodeKilled(Node* node) {
// Use this to cleanup our node
if (node->getType() == NODE_TYPE_AGENT) {
OctreeQueryNode* nodeData = (OctreeQueryNode*)node->getLinkedData();
if (nodeData) {
node->setLinkedData(NULL);
delete nodeData;
}
}
};
OctreeServer* OctreeServer::_theInstance = NULL;
OctreeServer::OctreeServer(const unsigned char* dataBuffer, int numBytes) :
ThreadedAssignment(dataBuffer, numBytes)
{
_argc = 0;
_argv = NULL;
_tree = NULL;
_packetsPerClientPerInterval = 10;
_wantVoxelPersist = true;
_wantLocalDomain = false;
_debugVoxelSending = false;
_shouldShowAnimationDebug = false;
_displayVoxelStats = false;
_debugVoxelReceiving = false;
_sendEnvironments = true;
_sendMinimalEnvironment = false;
_dumpVoxelsOnMove = false;
_wantPersist = true;
_debugSending = false;
_debugReceiving = false;
_verboseDebug = false;
_jurisdiction = NULL;
_jurisdictionSender = NULL;
_voxelServerPacketProcessor = NULL;
_voxelPersistThread = NULL;
_octreeInboundPacketProcessor = NULL;
_persistThread = NULL;
_parsedArgV = NULL;
_started = time(0);
@ -84,7 +68,7 @@ VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) :
_theInstance = this;
}
VoxelServer::~VoxelServer() {
OctreeServer::~OctreeServer() {
if (_parsedArgV) {
for (int i = 0; i < _argc; i++) {
delete[] _parsedArgV[i];
@ -93,7 +77,7 @@ VoxelServer::~VoxelServer() {
}
}
void VoxelServer::initMongoose(int port) {
void OctreeServer::initMongoose(int port) {
// setup the mongoose web server
struct mg_callbacks callbacks = {};
@ -113,10 +97,10 @@ void VoxelServer::initMongoose(int port) {
mg_start(&callbacks, NULL, options);
}
int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
int OctreeServer::civetwebRequestHandler(struct mg_connection* connection) {
const struct mg_request_info* ri = mg_get_request_info(connection);
VoxelServer* theServer = GetInstance();
OctreeServer* theServer = GetInstance();
#ifdef FORCE_CRASH
if (strcmp(ri->uri, "/force_crash") == 0 && strcmp(ri->request_method, "GET") == 0) {
@ -137,7 +121,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
}
if (strcmp(ri->uri, "/resetStats") == 0 && strcmp(ri->request_method, "GET") == 0) {
theServer->_voxelServerPacketProcessor->resetStats();
theServer->_octreeInboundPacketProcessor->resetStats();
showStats = true;
}
@ -148,7 +132,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
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 Voxel Server is running... <a href='/'>[RELOAD]</a></b>\r\n");
mg_printf(connection, "<b>Your %s Server is running... <a href='/'>[RELOAD]</a></b>\r\n", theServer->getMyServerName());
tm* localtm = localtime(&theServer->_started);
const int MAX_TIME_LENGTH = 128;
@ -198,7 +182,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
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);
mg_printf(connection, "%s File Loaded At: %s", theServer->getMyServerName(), buffer);
// Convert now to tm struct for UTC
tm* voxelsLoadedAtUTM = gmtime(theServer->getLoadCompleted());
@ -207,7 +191,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
mg_printf(connection, " [%s UTM] ", buffer);
}
} else {
mg_printf(connection, "%s", "Voxel Persist Disabled...\r\n");
mg_printf(connection, "%s File Persist Disabled...\r\n", theServer->getMyServerName());
}
mg_printf(connection, "%s", "\r\n");
@ -217,7 +201,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
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: ");
mg_printf(connection, "%s File Load Took: ", theServer->getMyServerName());
if (hours > 0) {
mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" );
}
@ -267,13 +251,13 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
// display outbound packet stats
mg_printf(connection, "%s", "<b>Voxel Packet Statistics...</b>\r\n");
uint64_t totalOutboundPackets = VoxelSendThread::_totalPackets;
uint64_t totalOutboundBytes = VoxelSendThread::_totalBytes;
uint64_t totalWastedBytes = VoxelSendThread::_totalWastedBytes;
uint64_t totalBytesOfOctalCodes = VoxelPacketData::getTotalBytesOfOctalCodes();
uint64_t totalBytesOfBitMasks = VoxelPacketData::getTotalBytesOfBitMasks();
uint64_t totalBytesOfColor = VoxelPacketData::getTotalBytesOfColor();
mg_printf(connection, "<b>%s Outbound Packet Statistics...</b>\r\n", theServer->getMyServerName());
uint64_t totalOutboundPackets = OctreeSendThread::_totalPackets;
uint64_t totalOutboundBytes = OctreeSendThread::_totalBytes;
uint64_t totalWastedBytes = OctreeSendThread::_totalWastedBytes;
uint64_t totalBytesOfOctalCodes = OctreePacketData::getTotalBytesOfOctalCodes();
uint64_t totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks();
uint64_t totalBytesOfColor = OctreePacketData::getTotalBytesOfColor();
const int COLUMN_WIDTH = 10;
mg_printf(connection, " Total Outbound Packets: %s packets\r\n",
@ -296,36 +280,37 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
mg_printf(connection, "%s", "\r\n");
// display inbound packet stats
mg_printf(connection, "%s", "<b>Voxel Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n");
uint64_t averageTransitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageTransitTimePerPacket();
uint64_t averageProcessTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageProcessTimePerPacket();
uint64_t averageLockWaitTimePerPacket = theServer->_voxelServerPacketProcessor->getAverageLockWaitTimePerPacket();
uint64_t averageProcessTimePerVoxel = theServer->_voxelServerPacketProcessor->getAverageProcessTimePerVoxel();
uint64_t averageLockWaitTimePerVoxel = theServer->_voxelServerPacketProcessor->getAverageLockWaitTimePerVoxel();
uint64_t totalVoxelsProcessed = theServer->_voxelServerPacketProcessor->getTotalVoxelsProcessed();
uint64_t totalPacketsProcessed = theServer->_voxelServerPacketProcessor->getTotalPacketsProcessed();
mg_printf(connection, "<b>%s Edit Statistics... <a href='/resetStats'>[RESET]</a></b>\r\n",
theServer->getMyServerName());
uint64_t averageTransitTimePerPacket = theServer->_octreeInboundPacketProcessor->getAverageTransitTimePerPacket();
uint64_t averageProcessTimePerPacket = theServer->_octreeInboundPacketProcessor->getAverageProcessTimePerPacket();
uint64_t averageLockWaitTimePerPacket = theServer->_octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket();
uint64_t averageProcessTimePerElement = theServer->_octreeInboundPacketProcessor->getAverageProcessTimePerElement();
uint64_t averageLockWaitTimePerElement = theServer->_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement();
uint64_t totalElementsProcessed = theServer->_octreeInboundPacketProcessor->getTotalElementsProcessed();
uint64_t totalPacketsProcessed = theServer->_octreeInboundPacketProcessor->getTotalPacketsProcessed();
float averageVoxelsPerPacket = totalPacketsProcessed == 0 ? 0 : totalVoxelsProcessed / totalPacketsProcessed;
float averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / 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 Voxels: %s voxels\r\n",
locale.toString((uint)totalVoxelsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Inbound Voxels/Packet: %f voxels/packet\r\n", averageVoxelsPerPacket);
mg_printf(connection, " Total Inbound Elements: %s elements\r\n",
locale.toString((uint)totalElementsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Inbound Elements/Packet: %f elements/packet\r\n", averageElementsPerPacket);
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/Voxel: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Voxel: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Element: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Element: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
int senderNumber = 0;
NodeToSenderStatsMap& allSenderStats = theServer->_voxelServerPacketProcessor->getSingleSenderStats();
NodeToSenderStatsMap& allSenderStats = theServer->_octreeInboundPacketProcessor->getSingleSenderStats();
for (NodeToSenderStatsMapIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
senderNumber++;
QUuid senderID = i->first;
@ -337,28 +322,28 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
averageTransitTimePerPacket = senderStats.getAverageTransitTimePerPacket();
averageProcessTimePerPacket = senderStats.getAverageProcessTimePerPacket();
averageLockWaitTimePerPacket = senderStats.getAverageLockWaitTimePerPacket();
averageProcessTimePerVoxel = senderStats.getAverageProcessTimePerVoxel();
averageLockWaitTimePerVoxel = senderStats.getAverageLockWaitTimePerVoxel();
totalVoxelsProcessed = senderStats.getTotalVoxelsProcessed();
averageProcessTimePerElement = senderStats.getAverageProcessTimePerElement();
averageLockWaitTimePerElement = senderStats.getAverageLockWaitTimePerElement();
totalElementsProcessed = senderStats.getTotalElementsProcessed();
totalPacketsProcessed = senderStats.getTotalPacketsProcessed();
averageVoxelsPerPacket = totalPacketsProcessed == 0 ? 0 : totalVoxelsProcessed / totalPacketsProcessed;
averageElementsPerPacket = totalPacketsProcessed == 0 ? 0 : totalElementsProcessed / 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 Voxels: %s voxels\r\n",
locale.toString((uint)totalVoxelsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Inbound Voxels/Packet: %f voxels/packet\r\n", averageVoxelsPerPacket);
mg_printf(connection, " Total Inbound Elements: %s elements\r\n",
locale.toString((uint)totalElementsProcessed).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Inbound Elements/Packet: %f elements/packet\r\n", averageElementsPerPacket);
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/Voxel: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Voxel: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerVoxel).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Process Time/Element: %s usecs\r\n",
locale.toString((uint)averageProcessTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
mg_printf(connection, " Average Wait Lock Time/Element: %s usecs\r\n",
locale.toString((uint)averageLockWaitTimePerElement).rightJustified(COLUMN_WIDTH, ' ').toLocal8Bit().constData());
}
@ -368,7 +353,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
// display memory usage stats
mg_printf(connection, "%s", "<b>Current Memory Usage Statistics</b>\r\n");
mg_printf(connection, "\r\nVoxelTreeElement size... %ld bytes\r\n", sizeof(VoxelTreeElement));
mg_printf(connection, "\r\nOctreeElement size... %ld bytes\r\n", sizeof(OctreeElement));
mg_printf(connection, "%s", "\r\n");
const char* memoryScaleLabel;
@ -383,7 +368,7 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
memoryScale = GIGABYTES;
}
mg_printf(connection, "Voxel Node Memory Usage: %8.2f %s\r\n",
mg_printf(connection, "Element Node Memory Usage: %8.2f %s\r\n",
OctreeElement::getVoxelMemoryUsage() / memoryScale, memoryScaleLabel);
mg_printf(connection, "Octcode Memory Usage: %8.2f %s\r\n",
OctreeElement::getOctcodeMemoryUsage() / memoryScale, memoryScaleLabel);
@ -459,18 +444,18 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) {
}
void VoxelServer::setArguments(int argc, char** argv) {
void OctreeServer::setArguments(int argc, char** argv) {
_argc = argc;
_argv = const_cast<const char**>(argv);
qDebug("VoxelServer::setArguments()\n");
qDebug("OctreeServer::setArguments()\n");
for (int i = 0; i < _argc; i++) {
qDebug("_argv[%d]=%s\n", i, _argv[i]);
}
}
void VoxelServer::parsePayload() {
void OctreeServer::parsePayload() {
if (getNumPayloadBytes() > 0) {
QString config((const char*) _payload);
@ -480,7 +465,7 @@ void VoxelServer::parsePayload() {
int argCount = configList.size() + 1;
qDebug("VoxelServer::parsePayload()... argCount=%d\n",argCount);
qDebug("OctreeServer::parsePayload()... argCount=%d\n",argCount);
_parsedArgV = new char*[argCount];
const char* dummy = "config-from-payload";
@ -491,17 +476,19 @@ void VoxelServer::parsePayload() {
QString configItem = configList.at(i-1);
_parsedArgV[i] = new char[configItem.length() + sizeof(char)];
strcpy(_parsedArgV[i], configItem.toLocal8Bit().constData());
qDebug("VoxelServer::parsePayload()... _parsedArgV[%d]=%s\n", i, _parsedArgV[i]);
qDebug("OctreeServer::parsePayload()... _parsedArgV[%d]=%s\n", i, _parsedArgV[i]);
}
setArguments(argCount, _parsedArgV);
}
}
void VoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
void OctreeServer::processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr) {
NodeList* nodeList = NodeList::getInstance();
if (dataByteArray[0] == PACKET_TYPE_VOXEL_QUERY) {
PACKET_TYPE packetType = dataByteArray[0];
if (packetType == getMyQueryMessageType()) {
bool debug = false;
if (debug) {
qDebug("Got PACKET_TYPE_VOXEL_QUERY at %llu.\n", usecTimestampNow());
@ -524,56 +511,38 @@ void VoxelServer::processDatagram(const QByteArray& dataByteArray, const HifiSoc
// this means they've heard from us and can reply, let's assume public is active
node->activatePublicSocket();
}
VoxelNodeData* nodeData = (VoxelNodeData*) node->getLinkedData();
if (nodeData && !nodeData->isVoxelSendThreadInitalized()) {
nodeData->initializeVoxelSendThread(this);
OctreeQueryNode* nodeData = (OctreeQueryNode*) node->getLinkedData();
if (nodeData && !nodeData->isOctreeSendThreadInitalized()) {
nodeData->initializeOctreeSendThread(this);
}
}
} else if (dataByteArray[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) {
if (_jurisdictionSender) {
_jurisdictionSender->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(),
dataByteArray.size());
}
} else if (_voxelServerPacketProcessor &&
(dataByteArray[0] == PACKET_TYPE_SET_VOXEL
|| dataByteArray[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE
|| dataByteArray[0] == PACKET_TYPE_ERASE_VOXEL)) {
const char* messageName;
switch (dataByteArray[0]) {
case PACKET_TYPE_SET_VOXEL:
messageName = "PACKET_TYPE_SET_VOXEL";
break;
case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE:
messageName = "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE";
break;
case PACKET_TYPE_ERASE_VOXEL:
messageName = "PACKET_TYPE_ERASE_VOXEL";
break;
}
_voxelServerPacketProcessor->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(),
} else if (_jurisdictionSender && packetType == PACKET_TYPE_JURISDICTION_REQUEST) {
_jurisdictionSender->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(),
dataByteArray.size());
} else if (_octreeInboundPacketProcessor && getOctree()->handlesEditPacketType(packetType)) {
_octreeInboundPacketProcessor->queueReceivedPacket(senderSockAddr, (unsigned char*) dataByteArray.data(),
dataByteArray.size());
} else {
// let processNodeData handle it.
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(),
dataByteArray.size());
}
} else {
// let processNodeData handle it.
NodeList::getInstance()->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(),
dataByteArray.size());
}
}
//int main(int argc, const char * argv[]) {
void VoxelServer::run() {
const char VOXEL_SERVER_LOGGING_TARGET_NAME[] = "voxel-server";
void OctreeServer::run() {
// Before we do anything else, create our tree...
_tree = createTree();
// change the logging target name while this is running
Logging::setTargetName(VOXEL_SERVER_LOGGING_TARGET_NAME);
Logging::setTargetName(getMyLoggingServerTargetName());
// Now would be a good time to parse our arguments, if we got them as assignment
if (getNumPayloadBytes() > 0) {
parsePayload();
}
beforeRun(); // after payload has been processed
qInstallMessageHandler(Logging::verboseMessageHandler);
const char* STATUS_PORT = "--statusPort";
@ -610,27 +579,8 @@ void VoxelServer::run() {
}
}
// should we send environments? Default is yes, but this command line suppresses sending
const char* DUMP_VOXELS_ON_MOVE = "--dumpVoxelsOnMove";
_dumpVoxelsOnMove = cmdOptionExists(_argc, _argv, DUMP_VOXELS_ON_MOVE);
qDebug("dumpVoxelsOnMove=%s\n", debug::valueOf(_dumpVoxelsOnMove));
// 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_VOXEL_SERVER);
nodeList->setOwnerType(getMyNodeType());
// 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};
@ -639,69 +589,52 @@ void VoxelServer::run() {
setvbuf(stdout, NULL, _IOLBF, 0);
// tell our NodeList about our desire to get notifications
nodeList->addHook(&_nodeWatcher);
nodeList->linkedDataCreateCallback = &attachVoxelNodeDataToNode;
nodeList->addHook(this);
nodeList->linkedDataCreateCallback = &OctreeServer::attachQueryNodeToNode;
nodeList->startSilentNodeRemovalThread();
srand((unsigned)time(0));
const char* DISPLAY_VOXEL_STATS = "--displayVoxelStats";
_displayVoxelStats = cmdOptionExists(_argc, _argv, DISPLAY_VOXEL_STATS);
qDebug("displayVoxelStats=%s\n", debug::valueOf(_displayVoxelStats));
const char* VERBOSE_DEBUG = "--verboseDebug";
_verboseDebug = cmdOptionExists(_argc, _argv, VERBOSE_DEBUG);
qDebug("verboseDebug=%s\n", debug::valueOf(_verboseDebug));
const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending";
_debugVoxelSending = cmdOptionExists(_argc, _argv, DEBUG_VOXEL_SENDING);
qDebug("debugVoxelSending=%s\n", debug::valueOf(_debugVoxelSending));
const char* DEBUG_SENDING = "--debugSending";
_debugSending = cmdOptionExists(_argc, _argv, DEBUG_SENDING);
qDebug("debugSending=%s\n", debug::valueOf(_debugSending));
const char* DEBUG_VOXEL_RECEIVING = "--debugVoxelReceiving";
_debugVoxelReceiving = cmdOptionExists(_argc, _argv, DEBUG_VOXEL_RECEIVING);
qDebug("debugVoxelReceiving=%s\n", debug::valueOf(_debugVoxelReceiving));
const char* DEBUG_RECEIVING = "--debugReceiving";
_debugReceiving = cmdOptionExists(_argc, _argv, DEBUG_RECEIVING);
qDebug("debugReceiving=%s\n", debug::valueOf(_debugReceiving));
const char* WANT_ANIMATION_DEBUG = "--shouldShowAnimationDebug";
_shouldShowAnimationDebug = cmdOptionExists(_argc, _argv, WANT_ANIMATION_DEBUG);
qDebug("shouldShowAnimationDebug=%s\n", debug::valueOf(_shouldShowAnimationDebug));
// By default we will voxel persist, if you want to disable this, then pass in this parameter
const char* NO_VOXEL_PERSIST = "--NoVoxelPersist";
if (cmdOptionExists(_argc, _argv, NO_VOXEL_PERSIST)) {
_wantVoxelPersist = false;
// By default we will persist, if you want to disable this, then pass in this parameter
const char* NO_PERSIST = "--NoPersist";
if (cmdOptionExists(_argc, _argv, NO_PERSIST)) {
_wantPersist = false;
}
qDebug("wantVoxelPersist=%s\n", debug::valueOf(_wantVoxelPersist));
qDebug("wantPersist=%s\n", debug::valueOf(_wantPersist));
// if we want Voxel Persistence, set up the local file and persist thread
if (_wantVoxelPersist) {
// if we want Persistence, set up the local file and persist thread
if (_wantPersist) {
// Check to see if the user passed in a command line option for setting packet send rate
const char* VOXELS_PERSIST_FILENAME = "--voxelsPersistFilename";
const char* voxelsPersistFilenameParameter = getCmdOption(_argc, _argv, VOXELS_PERSIST_FILENAME);
if (voxelsPersistFilenameParameter) {
strcpy(_voxelPersistFilename, voxelsPersistFilenameParameter);
const char* PERSIST_FILENAME = "--persistFilename";
const char* persistFilenameParameter = getCmdOption(_argc, _argv, PERSIST_FILENAME);
if (persistFilenameParameter) {
strcpy(_persistFilename, persistFilenameParameter);
} else {
//strcpy(voxelPersistFilename, _wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
strcpy(_voxelPersistFilename, LOCAL_VOXELS_PERSIST_FILE);
strcpy(_persistFilename, getMyDefaultPersistFilename());
}
qDebug("voxelPersistFilename=%s\n", _voxelPersistFilename);
qDebug("persistFilename=%s\n", _persistFilename);
// now set up VoxelPersistThread
_voxelPersistThread = new VoxelPersistThread(&_serverTree, _voxelPersistFilename);
if (_voxelPersistThread) {
_voxelPersistThread->initialize(true);
// now set up PersistThread
_persistThread = new OctreePersistThread(_tree, _persistFilename);
if (_persistThread) {
_persistThread->initialize(true);
}
}
// Check to see if the user passed in a command line option for loading an old style local
// Voxel File. If so, load it now. This is not the same as a voxel persist file
const char* INPUT_FILE = "-i";
const char* voxelsFilename = getCmdOption(_argc, _argv, INPUT_FILE);
if (voxelsFilename) {
_serverTree.readFromSVOFile(voxelsFilename);
}
// Check to see if the user passed in a command line option for setting packet send rate
const char* PACKETS_PER_SECOND = "--packetsPerSecond";
const char* packetsPerSecond = getCmdOption(_argc, _argv, PACKETS_PER_SECOND);
@ -721,10 +654,10 @@ void VoxelServer::run() {
_jurisdictionSender->initialize(true);
}
// set up our VoxelServerPacketProcessor
_voxelServerPacketProcessor = new VoxelServerPacketProcessor(this);
if (_voxelServerPacketProcessor) {
_voxelServerPacketProcessor->initialize(true);
// set up our OctreeServerPacketProcessor
_octreeInboundPacketProcessor = new OctreeInboundPacketProcessor(this);
if (_octreeInboundPacketProcessor) {
_octreeInboundPacketProcessor->initialize(true);
}
// Convert now to tm struct for local timezone
@ -766,23 +699,23 @@ void VoxelServer::run() {
delete _jurisdictionSender;
}
if (_voxelServerPacketProcessor) {
_voxelServerPacketProcessor->terminate();
delete _voxelServerPacketProcessor;
if (_octreeInboundPacketProcessor) {
_octreeInboundPacketProcessor->terminate();
delete _octreeInboundPacketProcessor;
}
if (_voxelPersistThread) {
_voxelPersistThread->terminate();
delete _voxelPersistThread;
if (_persistThread) {
_persistThread->terminate();
delete _persistThread;
}
// tell our NodeList we're done with notifications
nodeList->removeHook(&_nodeWatcher);
nodeList->removeHook(this);
delete _jurisdiction;
_jurisdiction = NULL;
qDebug() << "VoxelServer::run()... DONE\n";
qDebug() << "OctreeServer::run()... DONE\n";
}

View file

@ -0,0 +1,98 @@
//
// OctreeServer.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __octree_server__OctreeServer__
#define __octree_server__OctreeServer__
#include <QStringList>
#include <QDateTime>
#include <QtCore/QCoreApplication>
#include <ThreadedAssignment.h>
#include <EnvironmentData.h>
#include "OctreePersistThread.h"
#include "OctreeSendThread.h"
#include "OctreeServerConsts.h"
#include "OctreeInboundPacketProcessor.h"
/// Handles assignments of type OctreeServer - sending octrees to various clients.
class OctreeServer : public ThreadedAssignment, public NodeListHook {
public:
OctreeServer(const unsigned char* dataBuffer, int numBytes);
~OctreeServer();
/// allows setting of run arguments
void setArguments(int argc, char** argv);
bool wantsDebugSending() const { return _debugSending; }
bool wantsDebugReceiving() const { return _debugReceiving; }
bool wantsVerboseDebug() const { return _verboseDebug; }
Octree* getOctree() { return _tree; }
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; }
static OctreeServer* GetInstance() { return _theInstance; }
bool isInitialLoadComplete() const { return (_persistThread) ? _persistThread->isInitialLoadComplete() : true; }
time_t* getLoadCompleted() { return (_persistThread) ? _persistThread->getLoadCompleted() : NULL; }
uint64_t getLoadElapsedTime() const { return (_persistThread) ? _persistThread->getLoadElapsedTime() : 0; }
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode) = 0;
virtual Octree* createTree() = 0;
virtual unsigned char getMyNodeType() const = 0;
virtual PACKET_TYPE getMyQueryMessageType() const = 0;
virtual const char* getMyServerName() const = 0;
virtual const char* getMyLoggingServerTargetName() const = 0;
virtual const char* getMyDefaultPersistFilename() const = 0;
// subclass may implement these method
virtual void beforeRun() { };
virtual bool hasSpecialPacketToSend() { return false; }
virtual int sendSpecialPacket(Node* node) { return 0; }
static void attachQueryNodeToNode(Node* newNode);
// NodeListHook
virtual void nodeAdded(Node* node);
virtual void nodeKilled(Node* node);
public slots:
/// runs the voxel server assignment
void run();
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
protected:
int _argc;
const char** _argv;
char** _parsedArgV;
char _persistFilename[MAX_FILENAME_LENGTH];
int _packetsPerClientPerInterval;
Octree* _tree; // this IS a reaveraging tree
bool _wantPersist;
bool _debugSending;
bool _debugReceiving;
bool _verboseDebug;
JurisdictionMap* _jurisdiction;
JurisdictionSender* _jurisdictionSender;
OctreeInboundPacketProcessor* _octreeInboundPacketProcessor;
OctreePersistThread* _persistThread;
void parsePayload();
void initMongoose(int port);
static int civetwebRequestHandler(struct mg_connection *connection);
static OctreeServer* _theInstance;
time_t _started;
uint64_t _startedUSecs;
};
#endif // __octree_server__OctreeServer__

View file

@ -0,0 +1,21 @@
// OctreeServerConsts.h
// octree-server
//
// Created by Brad Hefta-Gaub on 12/4/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __octree_server__OctreeServerConsts__
#define __octree_server__OctreeServerConsts__
#include <SharedUtil.h>
#include <NodeList.h> // for MAX_PACKET_SIZE
#include <JurisdictionSender.h>
const int MAX_FILENAME_LENGTH = 1024;
const int INTERVALS_PER_SECOND = 60;
const int OCTREE_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND;
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels
#endif // __octree_server__OctreeServerConsts__

View file

@ -42,7 +42,7 @@ void JurisdictionListener::nodeKilled(Node* node) {
bool JurisdictionListener::queueJurisdictionRequest() {
static unsigned char buffer[MAX_PACKET_SIZE];
unsigned char* bufferOut = &buffer[0];
ssize_t sizeOut = populateTypeAndVersion(bufferOut, PACKET_TYPE_VOXEL_JURISDICTION_REQUEST);
ssize_t sizeOut = populateTypeAndVersion(bufferOut, PACKET_TYPE_JURISDICTION_REQUEST);
int nodeCount = 0;
NodeList* nodeList = NodeList::getInstance();
@ -65,7 +65,7 @@ bool JurisdictionListener::queueJurisdictionRequest() {
}
void JurisdictionListener::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION) {
if (packetData[0] == PACKET_TYPE_JURISDICTION) {
Node* node = NodeList::getInstance()->nodeWithAddress(senderAddress);
if (node) {
QUuid nodeUUID = node->getUUID();

View file

@ -17,8 +17,8 @@
#include "JurisdictionMap.h"
/// Sends out PACKET_TYPE_VOXEL_JURISDICTION_REQUEST packets to all voxel servers and then listens for and processes
/// the PACKET_TYPE_VOXEL_JURISDICTION packets it receives in order to maintain an accurate state of all jurisidictions
/// Sends out PACKET_TYPE_JURISDICTION_REQUEST packets to all voxel servers and then listens for and processes
/// the PACKET_TYPE_JURISDICTION packets it receives in order to maintain an accurate state of all jurisidictions
/// within the domain. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets
/// and adding them to the processing queue by calling queueReceivedPacket()
class JurisdictionListener : public NodeListHook, public PacketSender, public ReceivedPacketProcessor {
@ -39,7 +39,7 @@ public:
void nodeKilled(Node* node);
protected:
/// Callback for processing of received packets. Will process any queued PACKET_TYPE_VOXEL_JURISDICTION and update the
/// Callback for processing of received packets. Will process any queued PACKET_TYPE_JURISDICTION and update the
/// jurisdiction map member variable
/// \param sockaddr& senderAddress the address of the sender
/// \param packetData pointer to received data

View file

@ -263,7 +263,7 @@ bool JurisdictionMap::writeToFile(const char* filename) {
int JurisdictionMap::packEmptyJurisdictionIntoMessage(unsigned char* destinationBuffer, int availableBytes) {
unsigned char* bufferStart = destinationBuffer;
int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_VOXEL_JURISDICTION);
int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_JURISDICTION);
destinationBuffer += headerLength;
// No root or end node details to pack!
@ -277,7 +277,7 @@ int JurisdictionMap::packEmptyJurisdictionIntoMessage(unsigned char* destination
int JurisdictionMap::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) {
unsigned char* bufferStart = destinationBuffer;
int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_VOXEL_JURISDICTION);
int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_JURISDICTION);
destinationBuffer += headerLength;
// add the root jurisdiction

View file

@ -30,7 +30,7 @@ JurisdictionSender::~JurisdictionSender() {
void JurisdictionSender::processPacket(const HifiSockAddr& senderAddress, unsigned char* packetData, ssize_t packetLength) {
if (packetData[0] == PACKET_TYPE_VOXEL_JURISDICTION_REQUEST) {
if (packetData[0] == PACKET_TYPE_JURISDICTION_REQUEST) {
Node* node = NodeList::getInstance()->nodeWithAddress(senderAddress);
if (node) {
QUuid nodeUUID = node->getUUID();

View file

@ -17,7 +17,7 @@
#include <ReceivedPacketProcessor.h>
#include "JurisdictionMap.h"
/// Will process PACKET_TYPE_VOXEL_JURISDICTION_REQUEST packets and send out PACKET_TYPE_VOXEL_JURISDICTION packets
/// Will process PACKET_TYPE_JURISDICTION_REQUEST packets and send out PACKET_TYPE_JURISDICTION packets
/// to requesting parties. As with other ReceivedPacketProcessor classes the user is responsible for reading inbound packets
/// and adding them to the processing queue by calling queueReceivedPacket()
class JurisdictionSender : public PacketSender, public ReceivedPacketProcessor {

View file

@ -180,6 +180,10 @@ public:
virtual OctreeElement* createNewElement(unsigned char * octalCode = NULL) const = 0;
virtual bool handlesEditPacketType(PACKET_TYPE packetType) const { return false; }
virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
unsigned char* editData, int maxLength) { return 0; }
OctreeElement* getRoot() { return _rootNode; }
void eraseAllOctreeElements();
@ -250,7 +254,6 @@ public slots:
protected:
void deleteOctalCodeFromTreeRecursion(OctreeElement* node, void* extraData);
void readCodeColorBufferToTreeRecursion(OctreeElement* node, void* extraData);
int encodeTreeBitstreamRecursion(OctreeElement* node,
OctreePacketData* packetData, OctreeElementBag& bag,

View file

@ -379,7 +379,7 @@ void OctreeSceneStats::childBitsRemoved(bool includesExistsBits, bool includesCo
int OctreeSceneStats::packIntoMessage(unsigned char* destinationBuffer, int availableBytes) {
unsigned char* bufferStart = destinationBuffer;
int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_VOXEL_STATS);
int headerLength = populateTypeAndVersion(destinationBuffer, PACKET_TYPE_OCTREE_STATS);
destinationBuffer += headerLength;
memcpy(destinationBuffer, &_start, sizeof(_start));

View file

@ -0,0 +1,35 @@
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)
setup_hifi_library(${TARGET_NAME} ${OPTIONAL_SRCS})
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_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(particles ${TARGET_NAME} ${ROOT_DIR})

View file

@ -0,0 +1,22 @@
//
// ParticleNodeData.h
// hifi
//
// Created by Brad Hefta-Gaub on 12/4/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __hifi__ParticleNodeData__
#define __hifi__ParticleNodeData__
#include <OctreeQueryNode.h>
#include <PacketHeaders.h>
class ParticleNodeData : public OctreeQueryNode {
public:
ParticleNodeData(Node* owningNode) : OctreeQueryNode(owningNode) { };
virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_PARTICLE_DATA; }
};
#endif /* defined(__hifi__ParticleNodeData__) */

View file

@ -0,0 +1,37 @@
//
// ParticleServer.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 12/4/13
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <ParticleTree.h>
#include "ParticleServer.h"
#include "ParticleServerConsts.h"
#include "ParticleNodeData.h"
const char* PARTICLE_SERVER_NAME = "Particle";
const char* PARTICLE_SERVER_LOGGING_TARGET_NAME = "particle-server";
const char* LOCAL_PARTICLES_PERSIST_FILE = "resources/particles.svo";
ParticleServer::ParticleServer(const unsigned char* dataBuffer, int numBytes) : OctreeServer(dataBuffer, numBytes) {
// nothing special to do here...
}
ParticleServer::~ParticleServer() {
// nothing special to do here...
}
OctreeQueryNode* ParticleServer::createOctreeQueryNode(Node* newNode) {
return new ParticleNodeData(newNode);
}
Octree* ParticleServer::createTree() {
return new ParticleTree(true);
}
void ParticleServer::beforeRun() {
// nothing special to do...
}

View file

@ -0,0 +1,38 @@
//
// 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 <OctreeServer.h>
#include "ParticleServerConsts.h"
/// Handles assignments of type ParticleServer - sending particles to various clients.
class ParticleServer : public OctreeServer {
public:
ParticleServer(const unsigned char* dataBuffer, int numBytes);
~ParticleServer();
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode);
virtual Octree* createTree();
virtual unsigned char getMyNodeType() const { return NODE_TYPE_PARTICLE_SERVER; }
virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_PARTICLE_QUERY; }
virtual const char* getMyServerName() const { return PARTICLE_SERVER_NAME; }
virtual const char* getMyLoggingServerTargetName() const { return PARTICLE_SERVER_LOGGING_TARGET_NAME; }
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_PARTICLES_PERSIST_FILE; }
// subclass may implement these method
virtual void beforeRun();
private:
};
#endif // __particle_server__ParticleServer__

View file

@ -0,0 +1,16 @@
// ParticleServerConsts.h
// particle-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __particle_server__ParticleServerConsts__
#define __particle_server__ParticleServerConsts__
extern const char* PARTICLE_SERVER_NAME;
extern const char* PARTICLE_SERVER_LOGGING_TARGET_NAME;
extern const char* LOCAL_PARTICLES_PERSIST_FILE;
#endif // __particle_server__ParticleServerConsts__

View file

@ -0,0 +1,28 @@
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

@ -0,0 +1,19 @@
//
// ParticleTree.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#include "ParticleTree.h"
ParticleTree::ParticleTree(bool shouldReaverage) : Octree(shouldReaverage) {
_rootNode = createNewElement();
}
ParticleTreeElement* ParticleTree::createNewElement(unsigned char * octalCode) const {
ParticleTreeElement* newElement = new ParticleTreeElement(octalCode);
return newElement;
}

View file

@ -0,0 +1,24 @@
//
// ParticleTree.h
// hifi
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
#ifndef __hifi__ParticleTree__
#define __hifi__ParticleTree__
#include <Octree.h>
#include "ParticleTreeElement.h"
class ParticleTree : public Octree {
Q_OBJECT
public:
ParticleTree(bool shouldReaverage = false);
virtual ParticleTreeElement* createNewElement(unsigned char * octalCode = NULL) const;
ParticleTreeElement* getRoot() { return (ParticleTreeElement*)_rootNode; }
private:
};
#endif /* defined(__hifi__ParticleTree__) */

View file

@ -0,0 +1,62 @@
//
// ParticleTreeElement.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <QtCore/QDebug>
#include "ParticleTree.h"
#include "ParticleTreeElement.h"
ParticleTreeElement::ParticleTreeElement(unsigned char* octalCode) : OctreeElement() {
init(octalCode);
};
ParticleTreeElement::~ParticleTreeElement() {
_voxelMemoryUsage -= sizeof(ParticleTreeElement);
}
// This will be called primarily on addChildAt(), which means we're adding a child of our
// own type to our own tree. This means we should initialize that child with any tree and type
// specific settings that our children must have. One example is out VoxelSystem, which
// we know must match ours.
OctreeElement* ParticleTreeElement::createNewElement(unsigned char* octalCode) const {
ParticleTreeElement* newChild = new ParticleTreeElement(octalCode);
return newChild;
}
void ParticleTreeElement::init(unsigned char* octalCode) {
OctreeElement::init(octalCode);
_voxelMemoryUsage += sizeof(ParticleTreeElement);
}
bool ParticleTreeElement::appendElementData(OctreePacketData* packetData) const {
// will write to the encoded stream all of the contents of this element
return true;
}
int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args) {
// will read from the encoded stream all of the contents of this element
return 0;
}
// will average a "common reduced LOD view" from the the child elements...
void ParticleTreeElement::calculateAverageFromChildren() {
// nothing to do here yet...
}
// will detect if children are leaves AND collapsable into the parent node
// and in that case will collapse children and make this node
// a leaf, returns TRUE if all the leaves are collapsed into a
// single node
bool ParticleTreeElement::collapseChildren() {
// nothing to do here yet...
return false;
}

View file

@ -0,0 +1,45 @@
//
// ParticleTreeElement.h
// hifi
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __hifi__ParticleTreeElement__
#define __hifi__ParticleTreeElement__
#include <OctreeElement.h>
class ParticleTree;
class ParticleTreeElement;
class ParticleTreeElement : public OctreeElement {
friend class ParticleTree; // to allow createElement to new us...
ParticleTreeElement(unsigned char* octalCode = NULL);
virtual OctreeElement* createNewElement(unsigned char* octalCode = NULL) const;
public:
virtual ~ParticleTreeElement();
virtual void init(unsigned char * octalCode);
virtual bool hasContent() const { return isLeaf(); }
virtual void splitChildren() {}
virtual bool requiresSplit() const { return false; }
virtual bool appendElementData(OctreePacketData* packetData) const;
virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args);
virtual void calculateAverageFromChildren();
virtual bool collapseChildren();
virtual bool isRendered() const { return getShouldRender(); }
// type safe versions of OctreeElement methods
ParticleTreeElement* getChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::getChildAtIndex(index); }
ParticleTreeElement* addChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::addChildAtIndex(index); }
protected:
};
#endif /* defined(__hifi__ParticleTreeElement__) */

View file

@ -27,6 +27,8 @@ Assignment::Type Assignment::typeForNodeType(NODE_TYPE nodeType) {
return Assignment::AgentType;
case NODE_TYPE_VOXEL_SERVER:
return Assignment::VoxelServerType;
case NODE_TYPE_PARTICLE_SERVER:
return Assignment::ParticleServerType;
default:
return Assignment::AllTypes;
}
@ -176,6 +178,8 @@ const char* Assignment::getTypeName() const {
return "agent";
case Assignment::VoxelServerType:
return "voxel-server";
case Assignment::ParticleServerType:
return "particle-server";
default:
return "unknown";
}

View file

@ -28,6 +28,7 @@ public:
AvatarMixerType,
AgentType,
VoxelServerType,
ParticleServerType,
AllTypes
};

View file

@ -19,6 +19,8 @@
typedef char NODE_TYPE;
const NODE_TYPE NODE_TYPE_DOMAIN = 'D';
const NODE_TYPE NODE_TYPE_VOXEL_SERVER = 'V';
const NODE_TYPE NODE_TYPE_PARTICLE_SERVER = 'P';
const NODE_TYPE NODE_TYPE_ENVIRONMENT_SERVER = 'E';
const NODE_TYPE NODE_TYPE_AGENT = 'I';
const NODE_TYPE NODE_TYPE_AUDIO_MIXER = 'M';
const NODE_TYPE NODE_TYPE_AVATAR_MIXER = 'W';

View file

@ -28,7 +28,7 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
case PACKET_TYPE_AVATAR_FACE_VIDEO:
return 2;
case PACKET_TYPE_VOXEL_STATS:
case PACKET_TYPE_OCTREE_STATS:
return 2;
case PACKET_TYPE_DOMAIN:
@ -39,9 +39,9 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) {
case PACKET_TYPE_VOXEL_QUERY:
return 2;
case PACKET_TYPE_SET_VOXEL:
case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE:
case PACKET_TYPE_ERASE_VOXEL:
case PACKET_TYPE_VOXEL_SET:
case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE:
case PACKET_TYPE_VOXEL_ERASE:
return 1;
case PACKET_TYPE_VOXEL_DATA:

View file

@ -40,12 +40,16 @@ const PACKET_TYPE PACKET_TYPE_DATA_SERVER_SEND = 'u';
const PACKET_TYPE PACKET_TYPE_DATA_SERVER_CONFIRM = 'c';
const PACKET_TYPE PACKET_TYPE_VOXEL_QUERY = 'q';
const PACKET_TYPE PACKET_TYPE_VOXEL_DATA = 'V';
const PACKET_TYPE PACKET_TYPE_VOXEL_STATS = '#';
const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION = 'J';
const PACKET_TYPE PACKET_TYPE_VOXEL_JURISDICTION_REQUEST = 'j';
const PACKET_TYPE PACKET_TYPE_SET_VOXEL = 'S';
const PACKET_TYPE PACKET_TYPE_SET_VOXEL_DESTRUCTIVE = 'O';
const PACKET_TYPE PACKET_TYPE_ERASE_VOXEL = 'E';
const PACKET_TYPE PACKET_TYPE_VOXEL_SET = 'S';
const PACKET_TYPE PACKET_TYPE_VOXEL_SET_DESTRUCTIVE = 'O';
const PACKET_TYPE PACKET_TYPE_VOXEL_ERASE = 'E';
const PACKET_TYPE PACKET_TYPE_OCTREE_STATS = '#';
const PACKET_TYPE PACKET_TYPE_JURISDICTION = 'J';
const PACKET_TYPE PACKET_TYPE_JURISDICTION_REQUEST = 'j';
const PACKET_TYPE PACKET_TYPE_PARTICLE_QUERY = 'Q';
const PACKET_TYPE PACKET_TYPE_PARTICLE_DATA = 'v';
const PACKET_TYPE PACKET_TYPE_PARTICLE_ADD = 'a';
const PACKET_TYPE PACKET_TYPE_PARTICLE_ERASE = 'x';
typedef char PACKET_VERSION;

View file

@ -1,28 +0,0 @@
//
// NodeWatcher.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Node List Hook that watches for Node's being killed in order to clean up node specific memory and threads
//
#include <NodeList.h>
#include "NodeWatcher.h"
#include "VoxelNodeData.h"
void NodeWatcher::nodeAdded(Node* node) {
// do nothing
}
void NodeWatcher::nodeKilled(Node* node) {
// Use this to cleanup our node
if (node->getType() == NODE_TYPE_AGENT) {
VoxelNodeData* nodeData = (VoxelNodeData*)node->getLinkedData();
if (nodeData) {
node->setLinkedData(NULL);
delete nodeData;
}
}
};

View file

@ -1,23 +0,0 @@
//
// NodeWatcher.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Node List Hook that watches for Node's being killed in order to clean up node specific memory and threads
//
#ifndef __voxel_server__NodeWatcher__
#define __voxel_server__NodeWatcher__
#include <NodeList.h>
/// Voxel server's node watcher, which watches for nodes being killed and cleans up their data and threads
class NodeWatcher : public virtual NodeListHook {
public:
virtual void nodeAdded(Node* node);
virtual void nodeKilled(Node* node);
};
#endif // __voxel_server__NodeWatcher__

View file

@ -1,48 +0,0 @@
//
// VoxelSendThread.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded object for sending voxels to a client
//
#ifndef __voxel_server__VoxelSendThread__
#define __voxel_server__VoxelSendThread__
#include <GenericThread.h>
#include <NetworkPacket.h>
#include <VoxelTree.h>
#include <OctreeElementBag.h>
#include "VoxelNodeData.h"
#include "VoxelServer.h"
/// Threaded processor for sending voxel packets to a single client
class VoxelSendThread : public virtual GenericThread {
public:
VoxelSendThread(const QUuid& nodeUUID, VoxelServer* 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;
VoxelServer* _myServer;
int handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent);
int deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged);
unsigned char _tempOutputBuffer[MAX_VOXEL_PACKET_SIZE]; // used by environment sending code
VoxelPacketData _packetData;
};
#endif // __voxel_server__VoxelSendThread__

View file

@ -1,103 +0,0 @@
//
// VoxelServer.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __voxel_server__VoxelServer__
#define __voxel_server__VoxelServer__
#include <QStringList>
#include <QDateTime>
#include <QtCore/QCoreApplication>
#include <ThreadedAssignment.h>
#include <EnvironmentData.h>
#include "civetweb.h"
#include "NodeWatcher.h"
#include "VoxelPersistThread.h"
#include "VoxelSendThread.h"
#include "VoxelServerConsts.h"
#include "VoxelServerPacketProcessor.h"
/// Handles assignments of type VoxelServer - sending voxels to various clients.
class VoxelServer : public ThreadedAssignment {
public:
VoxelServer(const unsigned char* dataBuffer, int numBytes);
~VoxelServer();
/// allows setting of run arguments
void setArguments(int argc, char** argv);
bool wantsDebugVoxelSending() const { return _debugVoxelSending; }
bool wantsDebugVoxelReceiving() const { return _debugVoxelReceiving; }
bool wantsVerboseDebug() const { return _verboseDebug; }
bool wantShowAnimationDebug() const { return _shouldShowAnimationDebug; }
bool wantSendEnvironments() const { return _sendEnvironments; }
bool wantDumpVoxelsOnMove() const { return _dumpVoxelsOnMove; }
bool wantDisplayVoxelStats() const { return _displayVoxelStats; }
VoxelTree& getServerTree() { return _serverTree; }
JurisdictionMap* getJurisdiction() { return _jurisdiction; }
int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; }
bool getSendMinimalEnvironment() const { return _sendMinimalEnvironment; }
EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; }
int getEnvironmentDataCount() const { return sizeof(_environmentData)/sizeof(EnvironmentData); }
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; }
public slots:
/// runs the voxel server assignment
void run();
void processDatagram(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr);
private:
int _argc;
const char** _argv;
char** _parsedArgV;
char _voxelPersistFilename[MAX_FILENAME_LENGTH];
int _packetsPerClientPerInterval;
VoxelTree _serverTree; // this IS a reaveraging tree
bool _wantVoxelPersist;
bool _wantLocalDomain;
bool _debugVoxelSending;
bool _shouldShowAnimationDebug;
bool _displayVoxelStats;
bool _debugVoxelReceiving;
bool _sendEnvironments;
bool _sendMinimalEnvironment;
bool _dumpVoxelsOnMove;
bool _verboseDebug;
JurisdictionMap* _jurisdiction;
JurisdictionSender* _jurisdictionSender;
VoxelServerPacketProcessor* _voxelServerPacketProcessor;
VoxelPersistThread* _voxelPersistThread;
EnvironmentData _environmentData[3];
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 VoxelServer* _theInstance;
time_t _started;
uint64_t _startedUSecs;
};
#endif // __voxel_server__VoxelServer__

View file

@ -1,29 +0,0 @@
// VoxelServerConsts.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __voxel_server__VoxelServerConsts__
#define __voxel_server__VoxelServerConsts__
#include <SharedUtil.h>
#include <NodeList.h> // for MAX_PACKET_SIZE
#include <JurisdictionSender.h>
#include <VoxelTree.h>
#include "VoxelServerPacketProcessor.h"
const int MAX_FILENAME_LENGTH = 1024;
const int INTERVALS_PER_SECOND = 60;
const int VOXEL_SEND_INTERVAL_USECS = (1000 * 1000)/INTERVALS_PER_SECOND;
const int SENDING_TIME_TO_SPARE = 5 * 1000; // usec of sending interval to spare for calculating voxels
const int ENVIRONMENT_SEND_INTERVAL_USECS = 1000000;
extern const char* LOCAL_VOXELS_PERSIST_FILE;
extern const char* VOXELS_PERSIST_FILE;
#endif // __voxel_server__VoxelServerConsts__

View file

@ -1,223 +0,0 @@
//
// VoxelServerPacketProcessor.cpp
// voxel-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 voxel-server
//
#include <PacketHeaders.h>
#include <PerfStat.h>
#include "VoxelServer.h"
#include "VoxelServerConsts.h"
#include "VoxelServerPacketProcessor.h"
static QUuid DEFAULT_NODE_ID_REF;
VoxelServerPacketProcessor::VoxelServerPacketProcessor(VoxelServer* myServer) :
_myServer(myServer),
_receivedPacketCount(0),
_totalTransitTime(0),
_totalProcessTime(0),
_totalLockWaitTime(0),
_totalVoxelsInPacket(0),
_totalPackets(0)
{
}
void VoxelServerPacketProcessor::resetStats() {
_totalTransitTime = 0;
_totalProcessTime = 0;
_totalLockWaitTime = 0;
_totalVoxelsInPacket = 0;
_totalPackets = 0;
_singleSenderStats.clear();
}
void VoxelServerPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr,
unsigned char* packetData, ssize_t packetLength) {
bool debugProcessPacket = _myServer->wantsVerboseDebug();
if (debugProcessPacket) {
printf("VoxelServerPacketProcessor::processPacket() packetData=%p packetLength=%ld\n", packetData, packetLength);
}
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
if (packetData[0] == PACKET_TYPE_SET_VOXEL || packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE) {
bool destructive = (packetData[0] == PACKET_TYPE_SET_VOXEL_DESTRUCTIVE);
PerformanceWarning warn(_myServer->wantShowAnimationDebug(),
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
_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 voxelsInPacket = 0;
uint64_t processTime = 0;
uint64_t lockWaitTime = 0;
if (_myServer->wantShowAnimationDebug() || _myServer->wantsDebugVoxelReceiving()) {
printf("PROCESSING THREAD: got %s - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n",
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
_receivedPacketCount, packetLength, sequence, transitTime);
}
int atByte = numBytesPacketHeader + sizeof(sequence) + sizeof(sentAt);
unsigned char* voxelData = (unsigned char*)&packetData[atByte];
while (atByte < packetLength) {
int maxSize = packetLength - atByte;
if (debugProcessPacket) {
printf("VoxelServerPacketProcessor::processPacket() %s packetData=%p packetLength=%ld voxelData=%p atByte=%d maxSize=%d\n",
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
packetData, packetLength, voxelData, atByte, maxSize);
}
int octets = numberOfThreeBitSectionsInCode(voxelData, maxSize);
if (octets == OVERFLOWED_OCTCODE_BUFFER) {
printf("WARNING! Got voxel edit record that would overflow buffer in numberOfThreeBitSectionsInCode(), ");
printf("bailing processing of packet!\n");
break;
}
const int COLOR_SIZE_IN_BYTES = 3;
int voxelDataSize = bytesRequiredForCodeLength(octets) + COLOR_SIZE_IN_BYTES;
int voxelCodeSize = bytesRequiredForCodeLength(octets);
if (atByte + voxelDataSize <= packetLength) {
if (_myServer->wantShowAnimationDebug()) {
int red = voxelData[voxelCodeSize + RED_INDEX];
int green = voxelData[voxelCodeSize + GREEN_INDEX];
int blue = voxelData[voxelCodeSize + BLUE_INDEX];
float* vertices = firstVertexForCode(voxelData);
printf("inserting voxel: %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(voxelData, destructive);
_myServer->getServerTree().unlock();
uint64_t endProcess = usecTimestampNow();
voxelsInPacket++;
uint64_t thisProcessTime = endProcess - startProcess;
uint64_t thisLockWaitTime = startProcess - startLock;
processTime += thisProcessTime;
lockWaitTime += thisLockWaitTime;
// skip to next voxel edit record in the packet
voxelData += voxelDataSize;
atByte += voxelDataSize;
} else {
printf("WARNING! Got voxel edit record that would overflow buffer, bailing processing of packet!\n");
break;
}
}
if (debugProcessPacket) {
printf("VoxelServerPacketProcessor::processPacket() DONE LOOPING FOR %s packetData=%p packetLength=%ld voxelData=%p atByte=%d\n",
destructive ? "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE" : "PACKET_TYPE_SET_VOXEL",
packetData, packetLength, voxelData, atByte);
}
// Make sure our Node and NodeList knows we've heard from this node.
Node* senderNode = NodeList::getInstance()->nodeWithAddress(senderSockAddr);
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, voxelsInPacket, processTime, lockWaitTime);
} else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) {
_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->wantsDebugVoxelReceiving()) {
printf("PROCESSING THREAD: got PACKET_TYPE_ERASE_VOXEL - %d command from client receivedBytes=%ld sequence=%d transitTime=%llu usecs\n",
_receivedPacketCount, packetLength, sequence, transitTime);
}
// Send these bits off to the VoxelTree class to process them
_myServer->getServerTree().lockForWrite();
_myServer->getServerTree().processRemoveOctreeElementsBitstream((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(senderSockAddr);
if (node) {
node->setLastHeardMicrostamp(usecTimestampNow());
}
} else {
printf("unknown packet ignored... packetData[0]=%c\n", packetData[0]);
}
}
void VoxelServerPacketProcessor::trackInboundPackets(const QUuid& nodeUUID, int sequence, uint64_t transitTime,
int voxelsInPacket, uint64_t processTime, uint64_t lockWaitTime) {
_totalTransitTime += transitTime;
_totalProcessTime += processTime;
_totalLockWaitTime += lockWaitTime;
_totalVoxelsInPacket += voxelsInPacket;
_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._totalVoxelsInPacket += voxelsInPacket;
stats._totalPackets++;
_singleSenderStats[nodeUUID] = stats;
} else {
SingleSenderStats& stats = _singleSenderStats[nodeUUID];
stats._totalTransitTime += transitTime;
stats._totalProcessTime += processTime;
stats._totalLockWaitTime += lockWaitTime;
stats._totalVoxelsInPacket += voxelsInPacket;
stats._totalPackets++;
}
}
SingleSenderStats::SingleSenderStats() {
_totalTransitTime = 0;
_totalProcessTime = 0;
_totalLockWaitTime = 0;
_totalVoxelsInPacket = 0;
_totalPackets = 0;
}

View file

@ -0,0 +1,34 @@
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 voxel-server)
find_package(Qt5Widgets REQUIRED)
include(${MACRO_DIR}/SetupHifiLibrary.cmake)
setup_hifi_library(${TARGET_NAME} ${OPTIONAL_SRCS})
qt5_use_modules(${TARGET_NAME} Widgets)
include(${MACRO_DIR}/IncludeGLM.cmake)
include_glm(${TARGET_NAME} ${ROOT_DIR})
# link ZLIB
find_package(ZLIB)
include_directories(${ZLIB_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
# link in the shared library
include(${MACRO_DIR}/LinkHifiLibrary.cmake)
link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR})
# link in the hifi octree library
link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(octree-server ${TARGET_NAME} ${ROOT_DIR})
link_hifi_library(voxels ${TARGET_NAME} ${ROOT_DIR})

View file

@ -0,0 +1,21 @@
//
// VoxelNodeData.h
// hifi
//
// Created by Stephen Birarda on 3/21/13.
//
//
#ifndef __hifi__VoxelNodeData__
#define __hifi__VoxelNodeData__
#include <OctreeQueryNode.h>
#include <PacketHeaders.h>
class VoxelNodeData : public OctreeQueryNode {
public:
VoxelNodeData(Node* owningNode) : OctreeQueryNode(owningNode) { };
virtual PACKET_TYPE getMyPacketType() const { return PACKET_TYPE_VOXEL_DATA; }
};
#endif /* defined(__hifi__VoxelNodeData__) */

View file

@ -0,0 +1,71 @@
//
// VoxelServer.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 9/16/13.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <VoxelTree.h>
#include "VoxelServer.h"
#include "VoxelServerConsts.h"
#include "VoxelNodeData.h"
const char* VOXEL_SERVER_NAME = "Voxel";
const char* VOXEL_SERVER_LOGGING_TARGET_NAME = "voxel-server";
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.svo";
VoxelServer::VoxelServer(const unsigned char* dataBuffer, int numBytes) : OctreeServer(dataBuffer, numBytes) {
// nothing special to do here...
}
VoxelServer::~VoxelServer() {
// nothing special to do here...
}
OctreeQueryNode* VoxelServer::createOctreeQueryNode(Node* newNode) {
return new VoxelNodeData(newNode);
}
Octree* VoxelServer::createTree() {
return new VoxelTree(true);
}
bool VoxelServer::hasSpecialPacketToSend() {
bool shouldSendEnvironments = _sendEnvironments && shouldDo(ENVIRONMENT_SEND_INTERVAL_USECS, OCTREE_SEND_INTERVAL_USECS);
return shouldSendEnvironments;
}
int VoxelServer::sendSpecialPacket(Node* node) {
int numBytesPacketHeader = populateTypeAndVersion(_tempOutputBuffer, PACKET_TYPE_ENVIRONMENT_DATA);
int envPacketLength = numBytesPacketHeader;
int environmentsToSend = getSendMinimalEnvironment() ? 1 : getEnvironmentDataCount();
for (int i = 0; i < environmentsToSend; i++) {
envPacketLength += getEnvironmentData(i)->getBroadcastData(_tempOutputBuffer + envPacketLength);
}
NodeList::getInstance()->getNodeSocket().writeDatagram((char*) _tempOutputBuffer, envPacketLength,
node->getActiveSocket()->getAddress(),
node->getActiveSocket()->getPort());
return envPacketLength;
}
void VoxelServer::beforeRun() {
// 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 {
_sendEnvironments = true;
// 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));
}

View file

@ -0,0 +1,58 @@
//
// VoxelServer.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __voxel_server__VoxelServer__
#define __voxel_server__VoxelServer__
#include <QStringList>
#include <QDateTime>
#include <QtCore/QCoreApplication>
#include <ThreadedAssignment.h>
#include <EnvironmentData.h>
#include <OctreeServer.h>
#include "VoxelServerConsts.h"
/// Handles assignments of type VoxelServer - sending voxels to various clients.
class VoxelServer : public OctreeServer {
public:
VoxelServer(const unsigned char* dataBuffer, int numBytes);
~VoxelServer();
bool wantSendEnvironments() const { return _sendEnvironments; }
bool getSendMinimalEnvironment() const { return _sendMinimalEnvironment; }
EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; }
int getEnvironmentDataCount() const { return sizeof(_environmentData)/sizeof(EnvironmentData); }
// Subclasses must implement these methods
virtual OctreeQueryNode* createOctreeQueryNode(Node* newNode);
virtual Octree* createTree();
virtual unsigned char getMyNodeType() const { return NODE_TYPE_VOXEL_SERVER; }
virtual PACKET_TYPE getMyQueryMessageType() const { return PACKET_TYPE_VOXEL_QUERY; }
virtual const char* getMyServerName() const { return VOXEL_SERVER_NAME; }
virtual const char* getMyLoggingServerTargetName() const { return VOXEL_SERVER_LOGGING_TARGET_NAME; }
virtual const char* getMyDefaultPersistFilename() const { return LOCAL_VOXELS_PERSIST_FILE; }
// subclass may implement these method
virtual void beforeRun();
virtual bool hasSpecialPacketToSend();
virtual int sendSpecialPacket(Node* node);
private:
bool _sendEnvironments;
bool _sendMinimalEnvironment;
EnvironmentData _environmentData[3];
unsigned char _tempOutputBuffer[MAX_PACKET_SIZE];
};
#endif // __voxel_server__VoxelServer__

View file

@ -0,0 +1,18 @@
// VoxelServerConsts.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 8/21/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
//
#ifndef __voxel_server__VoxelServerConsts__
#define __voxel_server__VoxelServerConsts__
extern const char* VOXEL_SERVER_NAME;
extern const char* VOXEL_SERVER_LOGGING_TARGET_NAME;
extern const char* LOCAL_VOXELS_PERSIST_FILE;
const int ENVIRONMENT_SEND_INTERVAL_USECS = 1000000;
#endif // __voxel_server__VoxelServerConsts__

View file

@ -137,14 +137,14 @@ void VoxelEditPacketSender::queuePacketToNode(const QUuid& nodeUUID, unsigned ch
const char* messageName;
switch (buffer[0]) {
case PACKET_TYPE_SET_VOXEL:
messageName = "PACKET_TYPE_SET_VOXEL";
case PACKET_TYPE_VOXEL_SET:
messageName = "PACKET_TYPE_VOXEL_SET";
break;
case PACKET_TYPE_SET_VOXEL_DESTRUCTIVE:
messageName = "PACKET_TYPE_SET_VOXEL_DESTRUCTIVE";
case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE:
messageName = "PACKET_TYPE_VOXEL_SET_DESTRUCTIVE";
break;
case PACKET_TYPE_ERASE_VOXEL:
messageName = "PACKET_TYPE_ERASE_VOXEL";
case PACKET_TYPE_VOXEL_ERASE:
messageName = "PACKET_TYPE_VOXEL_ERASE";
break;
}
printf("VoxelEditPacketSender::queuePacketToNode() queued %s - command to node bytes=%ld sequence=%d transitTimeSoFar=%llu usecs\n",

View file

@ -355,7 +355,7 @@ void VoxelTree::nudgeLeaf(VoxelTreeElement* element, void* extraData) {
glm::vec3 nudge = args->nudgeVec;
// delete the old element
args->voxelEditSenderPtr->sendVoxelEditMessage(PACKET_TYPE_ERASE_VOXEL, voxelDetails);
args->voxelEditSenderPtr->sendVoxelEditMessage(PACKET_TYPE_VOXEL_ERASE, voxelDetails);
// nudge the old element
voxelDetails.x += nudge.x;
@ -363,7 +363,7 @@ void VoxelTree::nudgeLeaf(VoxelTreeElement* element, void* extraData) {
voxelDetails.z += nudge.z;
// create a new voxel in its stead
args->voxelEditSenderPtr->sendVoxelEditMessage(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE, voxelDetails);
args->voxelEditSenderPtr->sendVoxelEditMessage(PACKET_TYPE_VOXEL_SET_DESTRUCTIVE, voxelDetails);
}
// Recurses voxel element with an operation function
@ -638,3 +638,54 @@ void VoxelTree::readCodeColorBufferToTreeRecursion(VoxelTreeElement* node, ReadC
node->handleSubtreeChanged(this);
}
}
bool VoxelTree::handlesEditPacketType(PACKET_TYPE packetType) const {
// we handle these types of "edit" packets
switch (packetType) {
case PACKET_TYPE_VOXEL_SET:
case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE:
case PACKET_TYPE_VOXEL_ERASE:
return true;
}
return false;
}
int VoxelTree::processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
unsigned char* editData, int maxLength) {
int processedBytes = 0;
// we handle these types of "edit" packets
switch (packetType) {
case PACKET_TYPE_VOXEL_SET:
case PACKET_TYPE_VOXEL_SET_DESTRUCTIVE: {
bool destructive = (packetType == PACKET_TYPE_VOXEL_SET_DESTRUCTIVE);
int octets = numberOfThreeBitSectionsInCode(editData, maxLength);
if (octets == OVERFLOWED_OCTCODE_BUFFER) {
printf("WARNING! Got voxel edit record that would overflow buffer in numberOfThreeBitSectionsInCode(), ");
printf("bailing processing of packet!\n");
return processedBytes;
}
const int COLOR_SIZE_IN_BYTES = 3;
int voxelCodeSize = bytesRequiredForCodeLength(octets);
int voxelDataSize = voxelCodeSize + COLOR_SIZE_IN_BYTES;
if (voxelDataSize > maxLength) {
printf("WARNING! Got voxel edit record that would overflow buffer, bailing processing of packet!\n");
printf("bailing processing of packet!\n");
return processedBytes;
}
readCodeColorBufferToTree(editData, destructive);
return voxelDataSize;
} break;
case PACKET_TYPE_VOXEL_ERASE:
processRemoveOctreeElementsBitstream((unsigned char*)packetData, packetLength);
return maxLength;
}
return processedBytes;
}

View file

@ -53,6 +53,11 @@ public:
void readCodeColorBufferToTree(const unsigned char* codeColorBuffer, bool destructive = false);
virtual bool handlesEditPacketType(PACKET_TYPE packetType) const;
virtual int processEditPacketData(PACKET_TYPE packetType, unsigned char* packetData, int packetLength,
unsigned char* editData, int maxLength);
void processSetVoxelsBitstream(const unsigned char* bitstream, int bufferSizeBytes);
/**
signals:
void importSize(float x, float y, float z);