overte/voxel-server/src/main.cpp
2013-05-14 12:11:32 -07:00

594 lines
26 KiB
C++

//
// main.cpp
// Voxel Server
//
// Created by Stephen Birara on 03/06/13.
// Copyright (c) 2012 High Fidelity, Inc. All rights reserved.
//
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <OctalCode.h>
#include <AgentList.h>
#include <AgentTypes.h>
#include <EnvironmentData.h>
#include <VoxelTree.h>
#include "VoxelAgentData.h"
#include <SharedUtil.h>
#include <PacketHeaders.h>
#include <SceneUtils.h>
#ifdef _WIN32
#include "Syssocket.h"
#include "Systime.h"
#else
#include <sys/time.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#endif
const char* LOCAL_VOXELS_PERSIST_FILE = "resources/voxels.hio2";
const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxels.hio2";
const int VOXEL_LISTEN_PORT = 40106;
const int VOXEL_SIZE_BYTES = 3 + (3 * sizeof(float));
const int VOXELS_PER_PACKET = (MAX_PACKET_SIZE - 1) / VOXEL_SIZE_BYTES;
const int MIN_BRIGHTNESS = 64;
const float DEATH_STAR_RADIUS = 4.0;
const float MAX_CUBE = 0.05f;
const int VOXEL_SEND_INTERVAL_USECS = 100 * 1000;
int PACKETS_PER_CLIENT_PER_INTERVAL = 50;
const int MAX_VOXEL_TREE_DEPTH_LEVELS = 4;
VoxelTree randomTree;
bool wantVoxelPersist = true;
bool wantLocalDomain = false;
bool wantColorRandomizer = false;
bool debugVoxelSending = false;
EnvironmentData environmentData;
void randomlyFillVoxelTree(int levelsToGo, VoxelNode *currentRootNode) {
// randomly generate children for this node
// the first level of the tree (where levelsToGo = MAX_VOXEL_TREE_DEPTH_LEVELS) has all 8
if (levelsToGo > 0) {
bool createdChildren = false;
createdChildren = false;
for (int i = 0; i < 8; i++) {
if (true) {
// create a new VoxelNode to put here
currentRootNode->addChildAtIndex(i);
randomlyFillVoxelTree(levelsToGo - 1, currentRootNode->getChildAtIndex(i));
createdChildren = true;
}
}
if (!createdChildren) {
// we didn't create any children for this node, making it a leaf
// give it a random color
currentRootNode->setRandomColor(MIN_BRIGHTNESS);
} else {
// set the color value for this node
currentRootNode->setColorFromAverageOfChildren();
}
} else {
// this is a leaf node, just give it a color
currentRootNode->setRandomColor(MIN_BRIGHTNESS);
}
}
void eraseVoxelTreeAndCleanupAgentVisitData() {
// As our tree to erase all it's voxels
::randomTree.eraseAllVoxels();
// enumerate the agents clean up their marker nodes
for (AgentList::iterator agent = AgentList::getInstance()->begin(); agent != AgentList::getInstance()->end(); agent++) {
VoxelAgentData* agentData = (VoxelAgentData*) agent->getLinkedData();
if (agentData) {
// clean up the agent visit data
agentData->nodeBag.deleteAll();
}
}
}
// Version of voxel distributor that sends each LOD level at a time
void resInVoxelDistributor(AgentList* agentList,
AgentList::iterator& agent,
VoxelAgentData* agentData) {
ViewFrustum viewFrustum = agentData->getCurrentViewFrustum();
bool searchReset = false;
int searchLoops = 0;
int searchLevelWas = agentData->getMaxSearchLevel();
double start = usecTimestampNow();
while (!searchReset && agentData->nodeBag.isEmpty()) {
searchLoops++;
searchLevelWas = agentData->getMaxSearchLevel();
int maxLevelReached = randomTree.searchForColoredNodes(agentData->getMaxSearchLevel(), randomTree.rootNode,
viewFrustum, agentData->nodeBag);
agentData->setMaxLevelReached(maxLevelReached);
// If nothing got added, then we bump our levels.
if (agentData->nodeBag.isEmpty()) {
if (agentData->getMaxLevelReached() < agentData->getMaxSearchLevel()) {
agentData->resetMaxSearchLevel();
searchReset = true;
} else {
agentData->incrementMaxSearchLevel();
}
}
}
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printf("WARNING! searchForColoredNodes() took %lf seconds to identify %d nodes at level %d in %d loops\n",
elapsedsec, agentData->nodeBag.count(), searchLevelWas, searchLoops);
} else {
printf("WARNING! searchForColoredNodes() took %lf milliseconds to identify %d nodes at level %d in %d loops\n",
elapsedmsec, agentData->nodeBag.count(), searchLevelWas, searchLoops);
}
} else if (::debugVoxelSending) {
printf("searchForColoredNodes() took %lf milliseconds to identify %d nodes at level %d in %d loops\n",
elapsedmsec, agentData->nodeBag.count(), searchLevelWas, searchLoops);
}
// If we have something in our nodeBag, then turn them into packets and send them out...
if (!agentData->nodeBag.isEmpty()) {
static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
int bytesWritten = 0;
int packetsSentThisInterval = 0;
int truePacketsSent = 0;
int trueBytesSent = 0;
double start = usecTimestampNow();
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - 1) {
if (!agentData->nodeBag.isEmpty()) {
VoxelNode* subTree = agentData->nodeBag.extract();
bytesWritten = randomTree.encodeTreeBitstream(agentData->getMaxSearchLevel(), subTree,
&tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
agentData->nodeBag, &viewFrustum,
agentData->getWantColor());
if (agentData->getAvailable() >= bytesWritten) {
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
} else {
agentList->getAgentSocket().send(agent->getActiveSocket(),
agentData->getPacket(), agentData->getPacketLength());
trueBytesSent += agentData->getPacketLength();
truePacketsSent++;
packetsSentThisInterval++;
agentData->resetVoxelPacket();
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
}
} else {
if (agentData->isPacketWaiting()) {
agentList->getAgentSocket().send(agent->getActiveSocket(),
agentData->getPacket(), agentData->getPacketLength());
trueBytesSent += agentData->getPacketLength();
truePacketsSent++;
agentData->resetVoxelPacket();
}
packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left
}
}
// send the environment packet
int envPacketLength = environmentData.getBroadcastData(tempOutputBuffer);
agentList->getAgentSocket().send(agent->getActiveSocket(), tempOutputBuffer, envPacketLength);
trueBytesSent += envPacketLength;
truePacketsSent++;
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printf("WARNING! packetLoop() took %lf seconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count());
} else {
printf("WARNING! packetLoop() took %lf milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count());
}
} else if (::debugVoxelSending) {
printf("packetLoop() took %lf milliseconds to generate %d bytes in %d packets at level %d, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, searchLevelWas, agentData->nodeBag.count());
}
// if during this last pass, we emptied our bag, then we want to move to the next level.
if (agentData->nodeBag.isEmpty()) {
if (agentData->getMaxLevelReached() < agentData->getMaxSearchLevel()) {
agentData->resetMaxSearchLevel();
} else {
agentData->incrementMaxSearchLevel();
}
}
}
}
// Version of voxel distributor that sends the deepest LOD level at once
void deepestLevelVoxelDistributor(AgentList* agentList,
AgentList::iterator& agent,
VoxelAgentData* agentData,
bool viewFrustumChanged) {
int maxLevelReached = 0;
double start = usecTimestampNow();
// FOR NOW... agent tells us if it wants to receive only view frustum deltas
bool wantDelta = agentData->getWantDelta();
const ViewFrustum* lastViewFrustum = wantDelta ? &agentData->getLastKnownViewFrustum() : NULL;
printf("deepestLevelVoxelDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s\n",
viewFrustumChanged ? "yes" : "no",
agentData->nodeBag.isEmpty() ? "yes" : "no",
agentData->getViewSent() ? "yes" : "no"
);
// If the current view frustum has changed OR we have nothing to send, then search against
// the current view frustum for things to send.
if (viewFrustumChanged || agentData->nodeBag.isEmpty()) {
// If the bag was empty, then send everything in view, not just the delta
maxLevelReached = randomTree.searchForColoredNodes(INT_MAX, randomTree.rootNode, agentData->getCurrentViewFrustum(),
agentData->nodeBag, wantDelta, lastViewFrustum);
agentData->setViewSent(false);
}
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printf("WARNING! searchForColoredNodes() took %lf seconds to identify %d nodes at level %d\n",
elapsedsec, agentData->nodeBag.count(), maxLevelReached);
} else {
printf("WARNING! searchForColoredNodes() took %lf milliseconds to identify %d nodes at level %d\n",
elapsedmsec, agentData->nodeBag.count(), maxLevelReached);
}
} else if (::debugVoxelSending) {
printf("searchForColoredNodes() took %lf milliseconds to identify %d nodes at level %d\n",
elapsedmsec, agentData->nodeBag.count(), maxLevelReached);
}
// If we have something in our nodeBag, then turn them into packets and send them out...
if (!agentData->nodeBag.isEmpty()) {
static unsigned char tempOutputBuffer[MAX_VOXEL_PACKET_SIZE - 1]; // save on allocs by making this static
int bytesWritten = 0;
int packetsSentThisInterval = 0;
int truePacketsSent = 0;
int trueBytesSent = 0;
double start = usecTimestampNow();
while (packetsSentThisInterval < PACKETS_PER_CLIENT_PER_INTERVAL - 1) {
if (!agentData->nodeBag.isEmpty()) {
VoxelNode* subTree = agentData->nodeBag.extract();
bytesWritten = randomTree.encodeTreeBitstream(INT_MAX, subTree,
&tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1,
agentData->nodeBag, &agentData->getCurrentViewFrustum(),
agentData->getWantColor(), wantDelta, lastViewFrustum);
if (agentData->getAvailable() >= bytesWritten) {
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
} else {
agentList->getAgentSocket().send(agent->getActiveSocket(),
agentData->getPacket(), agentData->getPacketLength());
trueBytesSent += agentData->getPacketLength();
truePacketsSent++;
packetsSentThisInterval++;
agentData->resetVoxelPacket();
agentData->writeToPacket(&tempOutputBuffer[0], bytesWritten);
}
} else {
if (agentData->isPacketWaiting()) {
agentList->getAgentSocket().send(agent->getActiveSocket(),
agentData->getPacket(), agentData->getPacketLength());
trueBytesSent += agentData->getPacketLength();
truePacketsSent++;
agentData->resetVoxelPacket();
}
packetsSentThisInterval = PACKETS_PER_CLIENT_PER_INTERVAL; // done for now, no nodes left
}
}
// send the environment packet
int envPacketLength = environmentData.getBroadcastData(tempOutputBuffer);
agentList->getAgentSocket().send(agent->getActiveSocket(), tempOutputBuffer, envPacketLength);
trueBytesSent += envPacketLength;
truePacketsSent++;
double end = usecTimestampNow();
double elapsedmsec = (end - start)/1000.0;
if (elapsedmsec > 100) {
if (elapsedmsec > 1000) {
double elapsedsec = (end - start)/1000000.0;
printf("WARNING! packetLoop() took %lf seconds to generate %d bytes in %d packets %d nodes still to send\n",
elapsedsec, trueBytesSent, truePacketsSent, agentData->nodeBag.count());
} else {
printf("WARNING! packetLoop() took %lf milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, agentData->nodeBag.count());
}
} else if (::debugVoxelSending) {
printf("packetLoop() took %lf milliseconds to generate %d bytes in %d packets, %d nodes still to send\n",
elapsedmsec, trueBytesSent, truePacketsSent, agentData->nodeBag.count());
}
// if after sending packets we've emptied our bag, then we want to remember that we've sent all
// the voxels from the current view frustum
if (agentData->nodeBag.isEmpty()) {
agentData->updateLastKnownViewFrustum();
agentData->setViewSent(true);
}
} // end if bag wasn't empty, and so we sent stuff...
}
void persistVoxelsWhenDirty() {
// check the dirty bit and persist here...
if (::wantVoxelPersist && ::randomTree.isDirty()) {
printf("saving voxels to file...\n");
randomTree.writeToFileV2(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
randomTree.clearDirtyBit(); // tree is clean after saving
printf("DONE saving voxels to file...\n");
}
}
void *distributeVoxelsToListeners(void *args) {
AgentList* agentList = AgentList::getInstance();
timeval lastSendTime;
while (true) {
gettimeofday(&lastSendTime, NULL);
// enumerate the agents to send 3 packets to each
for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
VoxelAgentData* agentData = (VoxelAgentData*) agent->getLinkedData();
// Sometimes the agent data has not yet been linked, in which case we can't really do anything
if (agentData) {
bool viewFrustumChanged = agentData->updateCurrentViewFrustum();
printf("agentData->updateCurrentViewFrustum() viewFrustumChanged=%s\n", (viewFrustumChanged ? "yes" : "no"));
if (agentData->getWantResIn()) {
resInVoxelDistributor(agentList, agent, agentData);
} else {
deepestLevelVoxelDistributor(agentList, agent, agentData, viewFrustumChanged);
}
}
}
// dynamically sleep until we need to fire off the next set of voxels
double usecToSleep = VOXEL_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&lastSendTime));
if (usecToSleep > 0) {
usleep(usecToSleep);
} else {
std::cout << "Last send took too much time, not sleeping!\n";
}
}
pthread_exit(0);
}
void attachVoxelAgentDataToAgent(Agent *newAgent) {
if (newAgent->getLinkedData() == NULL) {
newAgent->setLinkedData(new VoxelAgentData());
}
}
int main(int argc, const char * argv[])
{
AgentList* agentList = AgentList::createInstance(AGENT_TYPE_VOXEL, VOXEL_LISTEN_PORT);
setvbuf(stdout, NULL, _IOLBF, 0);
// Handle Local Domain testing with the --local command line
const char* local = "--local";
::wantLocalDomain = cmdOptionExists(argc, argv,local);
if (::wantLocalDomain) {
printf("Local Domain MODE!\n");
int ip = getLocalAddress();
sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF));
}
agentList->linkedDataCreateCallback = &attachVoxelAgentDataToAgent;
agentList->startSilentAgentRemovalThread();
agentList->startDomainServerCheckInThread();
srand((unsigned)time(0));
const char* DEBUG_VOXEL_SENDING = "--debugVoxelSending";
::debugVoxelSending = cmdOptionExists(argc, argv, DEBUG_VOXEL_SENDING);
printf("debugVoxelSending=%s\n", (::debugVoxelSending ? "yes" : "no"));
const char* WANT_COLOR_RANDOMIZER = "--wantColorRandomizer";
::wantColorRandomizer = cmdOptionExists(argc, argv, WANT_COLOR_RANDOMIZER);
printf("wantColorRandomizer=%s\n", (::wantColorRandomizer ? "yes" : "no"));
// By default we will voxel persist, if you want to disable this, then pass in this parameter
const char* NO_VOXEL_PERSIST = "--NoVoxelPersist";
if (cmdOptionExists(argc, argv, NO_VOXEL_PERSIST)) {
::wantVoxelPersist = false;
}
printf("wantVoxelPersist=%s\n", (::wantVoxelPersist ? "yes" : "no"));
// if we want Voxel Persistance, load the local file now...
bool persistantFileRead = false;
if (::wantVoxelPersist) {
printf("loading voxels from file...\n");
persistantFileRead = ::randomTree.readFromFileV2(::wantLocalDomain ? LOCAL_VOXELS_PERSIST_FILE : VOXELS_PERSIST_FILE);
::randomTree.clearDirtyBit(); // the tree is clean since we just loaded it
printf("DONE loading voxels from file... fileRead=%s\n", persistantFileRead ? "yes" : "no" );
unsigned long nodeCount = ::randomTree.getVoxelCount();
printf("Nodes after loading scene %ld nodes\n", nodeCount);
}
// 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) {
randomTree.loadVoxelsFile(voxelsFilename,wantColorRandomizer);
}
// Check to see if the user passed in a command line option for setting packet send rate
const char* PACKETS_PER_SECOND = "--packetsPerSecond";
const char* packetsPerSecond = getCmdOption(argc, argv, PACKETS_PER_SECOND);
if (packetsPerSecond) {
PACKETS_PER_CLIENT_PER_INTERVAL = atoi(packetsPerSecond)/10;
if (PACKETS_PER_CLIENT_PER_INTERVAL < 1) {
PACKETS_PER_CLIENT_PER_INTERVAL = 1;
}
printf("packetsPerSecond=%s PACKETS_PER_CLIENT_PER_INTERVAL=%d\n", packetsPerSecond, PACKETS_PER_CLIENT_PER_INTERVAL);
}
const char* ADD_RANDOM_VOXELS = "--AddRandomVoxels";
if (cmdOptionExists(argc, argv, ADD_RANDOM_VOXELS)) {
// create an octal code buffer and load it with 0 so that the recursive tree fill can give
// octal codes to the tree nodes that it is creating
randomlyFillVoxelTree(MAX_VOXEL_TREE_DEPTH_LEVELS, randomTree.rootNode);
}
const char* ADD_SCENE = "--AddScene";
bool addScene = cmdOptionExists(argc, argv, ADD_SCENE);
const char* NO_ADD_SCENE = "--NoAddScene";
bool noAddScene = cmdOptionExists(argc, argv, NO_ADD_SCENE);
if (addScene && noAddScene) {
printf("WARNING! --AddScene and --NoAddScene are mutually exclusive. We will honor --NoAddScene\n");
}
// We will add a scene if...
// 1) we attempted to load a persistant file and it wasn't there
// 2) you asked us to add a scene
// HOWEVER -- we will NEVER add a scene if you explicitly tell us not to!
//
// TEMPORARILY DISABLED!!!
bool actuallyAddScene = false; // !noAddScene && (addScene || (::wantVoxelPersist && !persistantFileRead));
if (actuallyAddScene) {
addSphereScene(&randomTree);
}
pthread_t sendVoxelThread;
pthread_create(&sendVoxelThread, NULL, distributeVoxelsToListeners, NULL);
sockaddr agentPublicAddress;
unsigned char *packetData = new unsigned char[MAX_PACKET_SIZE];
ssize_t receivedBytes;
// loop to send to agents requesting data
while (true) {
// check to see if we need to persist our voxel state
persistVoxelsWhenDirty();
if (agentList->getAgentSocket().receive(&agentPublicAddress, packetData, &receivedBytes)) {
// XXXBHG: Hacked in support for 'S' SET command
if (packetData[0] == PACKET_HEADER_SET_VOXEL) {
unsigned short int itemNumber = (*((unsigned short int*)&packetData[1]));
printf("got I - insert voxels - command from client receivedBytes=%ld itemNumber=%d\n",
receivedBytes,itemNumber);
int atByte = 3;
unsigned char* pVoxelData = (unsigned char*)&packetData[3];
while (atByte < receivedBytes) {
unsigned char octets = (unsigned char)*pVoxelData;
int voxelDataSize = bytesRequiredForCodeLength(octets)+3; // 3 for color!
int voxelCodeSize = bytesRequiredForCodeLength(octets);
// color randomization on insert
int colorRandomizer = ::wantColorRandomizer ? randIntInRange (-50, 50) : 0;
int red = pVoxelData[voxelCodeSize+0];
int green = pVoxelData[voxelCodeSize+1];
int blue = pVoxelData[voxelCodeSize+2];
printf("insert voxels - wantColorRandomizer=%s old r=%d,g=%d,b=%d \n",
(::wantColorRandomizer?"yes":"no"),red,green,blue);
red = std::max(0,std::min(255,red + colorRandomizer));
green = std::max(0,std::min(255,green + colorRandomizer));
blue = std::max(0,std::min(255,blue + colorRandomizer));
printf("insert voxels - wantColorRandomizer=%s NEW r=%d,g=%d,b=%d \n",
(::wantColorRandomizer?"yes":"no"),red,green,blue);
pVoxelData[voxelCodeSize+0]=red;
pVoxelData[voxelCodeSize+1]=green;
pVoxelData[voxelCodeSize+2]=blue;
float* vertices = firstVertexForCode(pVoxelData);
printf("inserting voxel at: %f,%f,%f\n",vertices[0],vertices[1],vertices[2]);
delete []vertices;
randomTree.readCodeColorBufferToTree(pVoxelData);
// skip to next
pVoxelData+=voxelDataSize;
atByte+=voxelDataSize;
}
// after done inserting all these voxels, then reaverage colors
randomTree.reaverageVoxelColors(randomTree.rootNode);
}
if (packetData[0] == PACKET_HEADER_ERASE_VOXEL) {
// Send these bits off to the VoxelTree class to process them
printf("got Erase Voxels message, have voxel tree do the work... randomTree.processRemoveVoxelBitstream()\n");
randomTree.processRemoveVoxelBitstream((unsigned char*)packetData,receivedBytes);
// Now send this to the connected agents so they know to delete
printf("rebroadcasting delete voxel message to connected agents... agentList.broadcastToAgents()\n");
agentList->broadcastToAgents(packetData,receivedBytes, &AGENT_TYPE_AVATAR, 1);
}
if (packetData[0] == PACKET_HEADER_Z_COMMAND) {
// the Z command is a special command that allows the sender to send the voxel server high level semantic
// requests, like erase all, or add sphere scene
char* command = (char*) &packetData[1]; // start of the command
int commandLength = strlen(command); // commands are null terminated strings
int totalLength = 1+commandLength+1;
printf("got Z message len(%ld)= %s\n",receivedBytes,command);
while (totalLength <= receivedBytes) {
if (0==strcmp(command,(char*)"erase all")) {
printf("got Z message == erase all\n");
eraseVoxelTreeAndCleanupAgentVisitData();
}
if (0==strcmp(command,(char*)"add scene")) {
printf("got Z message == add scene\n");
addSphereScene(&randomTree);
}
totalLength += commandLength+1;
}
// Now send this to the connected agents so they can also process these messages
printf("rebroadcasting Z message to connected agents... agentList.broadcastToAgents()\n");
agentList->broadcastToAgents(packetData,receivedBytes, &AGENT_TYPE_AVATAR, 1);
}
// If we got a PACKET_HEADER_HEAD_DATA, then we're talking to an AGENT_TYPE_AVATAR, and we
// need to make sure we have it in our agentList.
if (packetData[0] == PACKET_HEADER_HEAD_DATA) {
uint16_t agentID = 0;
unpackAgentId(packetData + sizeof(PACKET_HEADER_HEAD_DATA), &agentID);
agentList->addOrUpdateAgent(&agentPublicAddress, &agentPublicAddress, AGENT_TYPE_AVATAR, agentID);
agentList->updateAgentWithData(&agentPublicAddress, packetData, receivedBytes);
}
}
}
pthread_join(sendVoxelThread, NULL);
return 0;
}