From 04c4dabd3d14010b03727aa95edea946f621f076 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 20 Sep 2013 14:35:25 -0700 Subject: [PATCH 1/6] unblock socket receive in Agent, add destructive voxel add to VS scripting --- assignment-client/src/Agent.cpp | 4 +++- assignment-client/src/main.cpp | 3 +++ .../src/voxels/VoxelScriptingInterface.cpp | 17 ++++++++++++++++- .../src/voxels/VoxelScriptingInterface.h | 12 ++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 5ba022d3a6..aa9bfae31f 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -23,6 +23,8 @@ void Agent::run() { NodeList::getInstance()->setOwnerType(NODE_TYPE_AGENT); NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1); + NodeList::getInstance()->getNodeSocket()->setBlocking(false); + QNetworkAccessManager manager; // figure out the URL for the script for this agent assignment @@ -83,7 +85,7 @@ void Agent::run() { // flush the voxel packet queue voxelScripter.getVoxelPacketSender()->process(); - if (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) { + while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) { NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); } diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 9c106ab79a..1777599c3b 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -103,6 +103,9 @@ void childClient() { nodeList->setOwnerType(NODE_TYPE_UNASSIGNED); nodeList->reset(); + // set the NodeList socket back to blocking + nodeList->getNodeSocket()->setBlocking(true); + // reset the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(CHILD_TARGET_NAME); } diff --git a/assignment-client/src/voxels/VoxelScriptingInterface.cpp b/assignment-client/src/voxels/VoxelScriptingInterface.cpp index 343cb01a44..0335dde221 100644 --- a/assignment-client/src/voxels/VoxelScriptingInterface.cpp +++ b/assignment-client/src/voxels/VoxelScriptingInterface.cpp @@ -8,8 +8,23 @@ #include "VoxelScriptingInterface.h" +void VoxelScriptingInterface::queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails) { + _voxelPacketSender.sendVoxelEditMessage(addPacketType, addVoxelDetails); +} + void VoxelScriptingInterface::queueVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue) { // setup a VoxelDetail struct with the data VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue}; - _voxelPacketSender.sendVoxelEditMessage(PACKET_TYPE_SET_VOXEL, addVoxelDetail); + + // queue the packet + queueVoxelAdd(PACKET_TYPE_SET_VOXEL, addVoxelDetail); +} + +void VoxelScriptingInterface::queueDesctructiveVoxelAdd(float x, float y, float z, float scale, + uchar red, uchar green, uchar blue) { + // setup a VoxelDetail struct with the data + VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue}; + + // queue the destructive add + queueVoxelAdd(PACKET_TYPE_SET_VOXEL_DESTRUCTIVE, addVoxelDetail); } diff --git a/assignment-client/src/voxels/VoxelScriptingInterface.h b/assignment-client/src/voxels/VoxelScriptingInterface.h index 00be3d536f..00d1b065f0 100644 --- a/assignment-client/src/voxels/VoxelScriptingInterface.h +++ b/assignment-client/src/voxels/VoxelScriptingInterface.h @@ -28,9 +28,21 @@ public slots: /// \param green the G value for RGB color of voxel /// \param blue the B value for RGB color of voxel void queueVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue); + + /// queues the destructive creation of a voxel which will be sent by calling process on the PacketSender + /// \param x the x-coordinate of the voxel (in VS space) + /// \param y the y-coordinate of the voxel (in VS space) + /// \param z the z-coordinate of the voxel (in VS space) + /// \param scale the scale of the voxel (in VS space) + /// \param red the R value for RGB color of voxel + /// \param green the G value for RGB color of voxel + /// \param blue the B value for RGB color of voxel + void queueDesctructiveVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue); private: /// attached VoxelEditPacketSender that handles queuing and sending of packets to VS VoxelEditPacketSender _voxelPacketSender; + + void queueVoxelAdd(PACKET_TYPE addPacketType, VoxelDetail& addVoxelDetails); }; #endif /* defined(__hifi__VoxelScriptingInterface__) */ From 6533d7fcde35fc8bbe11dc66dc4d5675f0fdecae Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 20 Sep 2013 14:40:09 -0700 Subject: [PATCH 2/6] add method to packet sender to flush queue without sleep --- assignment-client/src/Agent.cpp | 4 ++-- libraries/shared/src/PacketSender.cpp | 19 +++++++++++++++++++ libraries/shared/src/PacketSender.h | 1 + 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index aa9bfae31f..7ace955ccd 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -82,8 +82,8 @@ void Agent::run() { // allow the scripter's call back to setup visual data emit preSendCallback(); - // flush the voxel packet queue - voxelScripter.getVoxelPacketSender()->process(); + // flush the voxel packet queue, don't allow it to sleep us + voxelScripter.getVoxelPacketSender()->processWithoutSleep(); while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) { NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); diff --git a/libraries/shared/src/PacketSender.cpp b/libraries/shared/src/PacketSender.cpp index 511b01541f..ebf3b50060 100644 --- a/libraries/shared/src/PacketSender.cpp +++ b/libraries/shared/src/PacketSender.cpp @@ -68,3 +68,22 @@ bool PacketSender::process() { } return isStillRunning(); // keep running till they terminate us } + +void PacketSender::processWithoutSleep() { + while (_packets.size() > 0) { + NetworkPacket& packet = _packets.front(); + + // send the packet through the NodeList... + UDPSocket* nodeSocket = NodeList::getInstance()->getNodeSocket(); + + nodeSocket->send(&packet.getAddress(), packet.getData(), packet.getLength()); + + if (_notify) { + _notify->packetSentNotification(packet.getLength()); + } + + lock(); + _packets.erase(_packets.begin()); + unlock(); + } +} diff --git a/libraries/shared/src/PacketSender.h b/libraries/shared/src/PacketSender.h index 3a07444f2b..5309043743 100644 --- a/libraries/shared/src/PacketSender.h +++ b/libraries/shared/src/PacketSender.h @@ -43,6 +43,7 @@ public: PacketSenderNotify* getPacketSenderNotify() const { return _notify; } virtual bool process(); + virtual void processWithoutSleep(); protected: int _packetsPerSecond; From 3d1c77d04796b19a55bea4a9394a8521f872b553 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 20 Sep 2013 14:47:36 -0700 Subject: [PATCH 3/6] expose the TREE_SCALE to JS --- assignment-client/src/Agent.cpp | 4 ++++ .../resources/web/assignment/placeholder.js | 19 ++----------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 7ace955ccd..cfdb34208c 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "Agent.h" #include "voxels/VoxelScriptingInterface.h" @@ -52,6 +53,9 @@ void Agent::run() { QScriptValue voxelScripterValue = engine.newQObject(&voxelScripter); engine.globalObject().setProperty("Voxels", voxelScripterValue); + QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); + engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); + qDebug() << "Downloaded script:" << scriptString << "\n"; qDebug() << "Evaluated script:" << engine.evaluate(scriptString).toString() << "\n"; diff --git a/domain-server/resources/web/assignment/placeholder.js b/domain-server/resources/web/assignment/placeholder.js index f106fbc9ba..93f90b7b30 100644 --- a/domain-server/resources/web/assignment/placeholder.js +++ b/domain-server/resources/web/assignment/placeholder.js @@ -1,20 +1,5 @@ -/* Add your JavaScript for assignment below this line */ +// Add your JavaScript for assignment below this line -// here are some examples of things you can call -Avatar.position = {x: 0, y: 0.565925, z: 10}; -Avatar.chatMessage = "I am not a robot!"; -Avatar.handPosition = {x: 0, y: 4.5, z: 0}; +// The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life) -// here I'm creating a function to fire before each data send -function dance() { - // switch the body yaw from 1 to 90 - var randomAngle = Math.floor(Math.random() * 90); - if (Math.random() < 0.5) { - randomAngle * -1; - } - - Avatar.bodyYaw = randomAngle; -} -// register the call back so it fires before each data send -Agent.preSendCallback.connect(dance); \ No newline at end of file From dd4215d0fde43ff49feccc92a74daa9a59a50df0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 23 Sep 2013 09:55:31 -0700 Subject: [PATCH 4/6] add a destructive voxel add command to VoxelScriptingInterface --- assignment-client/src/voxels/VoxelScriptingInterface.cpp | 2 +- assignment-client/src/voxels/VoxelScriptingInterface.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/voxels/VoxelScriptingInterface.cpp b/assignment-client/src/voxels/VoxelScriptingInterface.cpp index 0335dde221..8c31d0b5f8 100644 --- a/assignment-client/src/voxels/VoxelScriptingInterface.cpp +++ b/assignment-client/src/voxels/VoxelScriptingInterface.cpp @@ -20,7 +20,7 @@ void VoxelScriptingInterface::queueVoxelAdd(float x, float y, float z, float sca queueVoxelAdd(PACKET_TYPE_SET_VOXEL, addVoxelDetail); } -void VoxelScriptingInterface::queueDesctructiveVoxelAdd(float x, float y, float z, float scale, +void VoxelScriptingInterface::queueDestructiveVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue) { // setup a VoxelDetail struct with the data VoxelDetail addVoxelDetail = {x, y, z, scale, red, green, blue}; diff --git a/assignment-client/src/voxels/VoxelScriptingInterface.h b/assignment-client/src/voxels/VoxelScriptingInterface.h index 00d1b065f0..6ac2cd6bfe 100644 --- a/assignment-client/src/voxels/VoxelScriptingInterface.h +++ b/assignment-client/src/voxels/VoxelScriptingInterface.h @@ -37,7 +37,7 @@ public slots: /// \param red the R value for RGB color of voxel /// \param green the G value for RGB color of voxel /// \param blue the B value for RGB color of voxel - void queueDesctructiveVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue); + void queueDestructiveVoxelAdd(float x, float y, float z, float scale, uchar red, uchar green, uchar blue); private: /// attached VoxelEditPacketSender that handles queuing and sending of packets to VS VoxelEditPacketSender _voxelPacketSender; From 77193dd7073bfb1d2d2bdd64eff7d1c11beda1ac Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 23 Sep 2013 09:56:07 -0700 Subject: [PATCH 5/6] change the placeholder javascript to be 32x32 game of life --- .../resources/web/assignment/placeholder.js | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/domain-server/resources/web/assignment/placeholder.js b/domain-server/resources/web/assignment/placeholder.js index 93f90b7b30..e351f5e2b7 100644 --- a/domain-server/resources/web/assignment/placeholder.js +++ b/domain-server/resources/web/assignment/placeholder.js @@ -2,4 +2,125 @@ // The following is an example of Conway's Game of Life (http://en.wikipedia.org/wiki/Conway's_Game_of_Life) +var NUMBER_OF_CELLS_EACH_DIMENSION = 32; +var NUMBER_OF_CELLS = NUMBER_OF_CELLS_EACH_DIMENSION * NUMBER_OF_CELLS_EACH_DIMENSION; +var currentCells = []; +var nextCells = []; + +var METER_LENGTH = 1 / TREE_SCALE; +var cellScale = (8 * METER_LENGTH) / NUMBER_OF_CELLS_EACH_DIMENSION; + +print("TREE_SCALE = " + TREE_SCALE + "\n"); + +// randomly populate the cell start values +for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + // create the array to hold this row + currentCells[i] = []; + + // create the array to hold this row in the nextCells array + nextCells[i] = []; + + for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + currentCells[i][j] = Math.floor(Math.random() * 2); + + // put the same value in the nextCells array for first board draw + nextCells[i][j] = currentCells[i][j]; + } +} + +function isNeighbourAlive(i, j) { + if (i < 0 || i >= NUMBER_OF_CELLS_EACH_DIMENSION + || i < 0 || j >= NUMBER_OF_CELLS_EACH_DIMENSION) { + return 0; + } else { + return currentCells[i][j]; + } +} + +function updateCells() { + var i = 0; + var j = 0; + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + // figure out the number of live neighbours for the i-j cell + var liveNeighbours = + isNeighbourAlive(i + 1, j - 1) + isNeighbourAlive(i + 1, j) + isNeighbourAlive(i + 1, j + 1) + + isNeighbourAlive(i, j - 1) + isNeighbourAlive(i, j + 1) + + isNeighbourAlive(i - 1, j - 1) + isNeighbourAlive(i - 1, j) + isNeighbourAlive(i - 1, j + 1); + + if (currentCells[i][j]) { + // live cell + + if (liveNeighbours < 2) { + // rule #1 - under-population - this cell will die + // mark it zero to mark the change + nextCells[i][j] = 0; + } else if (liveNeighbours < 4) { + // rule #2 - this cell lives + // mark it -1 to mark no change + nextCells[i][j] = -1; + } else { + // rule #3 - overcrowding - this cell dies + // mark it zero to mark the change + nextCells[i][j] = 0; + } + } else { + // dead cell + if (liveNeighbours == 3) { + // rule #4 - reproduction - this cell revives + // mark it one to mark the change + nextCells[i][j] = 1; + } else { + // this cell stays dead + // mark it -1 for no change + nextCells[i][j] = -1; + } + } + } + } + + for (i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (nextCells[i][j] != -1) { + // there has been a change to this cell, change the value in the currentCells array + currentCells[i][j] = nextCells[i][j]; + } + } + } +} + +function sendNextCells() { + for (var i = 0; i < NUMBER_OF_CELLS_EACH_DIMENSION; i++) { + for (var j = 0; j < NUMBER_OF_CELLS_EACH_DIMENSION; j++) { + if (nextCells[i][j] != -1) { + // there has been a change to the state of this cell, send it + + // find the x and y position for this voxel, z = 0 + var x = j * cellScale; + var y = i * cellScale; + + // queue a packet to add a voxel for the new cell + var color = (nextCells[i][j] == 1) ? 255 : 1; + Voxels.queueDestructiveVoxelAdd(x, y, 0, cellScale, color, color, color); + } + } + } +} + +var sentFirstBoard = false; + +function step() { + if (sentFirstBoard) { + // we've already sent the first full board, perform a step in time + updateCells(); + } else { + // this will be our first board send + sentFirstBoard = true; + } + + sendNextCells(); +} + +Agent.willSendVisualDataCallback.connect(step); \ No newline at end of file From 313410ae0dd08517a523fca6ef7a0d8c26da5e1e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 23 Sep 2013 09:57:05 -0700 Subject: [PATCH 6/6] updates to Agent class needed for game of life --- assignment-client/src/Agent.cpp | 49 +++++++++++++++++++++++++-------- assignment-client/src/Agent.h | 2 +- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index cfdb34208c..aabf9fba66 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -21,10 +21,11 @@ Agent::Agent(const unsigned char* dataBuffer, int numBytes) : Assignment(dataBuf } void Agent::run() { - NodeList::getInstance()->setOwnerType(NODE_TYPE_AGENT); - NodeList::getInstance()->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1); - - NodeList::getInstance()->getNodeSocket()->setBlocking(false); + NodeList* nodeList = NodeList::getInstance(); + nodeList->setOwnerType(NODE_TYPE_AGENT); + nodeList->setNodeTypesOfInterest(&NODE_TYPE_VOXEL_SERVER, 1); + + nodeList->getNodeSocket()->setBlocking(false); QNetworkAccessManager manager; @@ -56,19 +57,30 @@ void Agent::run() { QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); + const long long VISUAL_DATA_SEND_INTERVAL_USECS = (1 / 60.0f) * 1000 * 1000; + + QScriptValue visualSendIntervalValue = engine.newVariant((QVariant(VISUAL_DATA_SEND_INTERVAL_USECS / 1000))); + engine.globalObject().setProperty("VISUAL_DATA_SEND_INTERVAL_MS", visualSendIntervalValue); + qDebug() << "Downloaded script:" << scriptString << "\n"; - qDebug() << "Evaluated script:" << engine.evaluate(scriptString).toString() << "\n"; + QScriptValue result = engine.evaluate(scriptString); + qDebug() << "Evaluated script.\n"; + + if (engine.hasUncaughtException()) { + int line = engine.uncaughtExceptionLineNumber(); + qDebug() << "Uncaught exception at line" << line << ":" << result.toString() << "\n"; + } timeval thisSend; timeval lastDomainServerCheckIn = {}; int numMicrosecondsSleep = 0; - const long long DATA_SEND_INTERVAL_USECS = (1 / 60.0f) * 1000 * 1000; - sockaddr_in senderAddress; unsigned char receivedData[MAX_PACKET_SIZE]; ssize_t receivedBytes; + bool hasVoxelServer = false; + while (!_shouldStop) { // update the thisSend timeval to the current time gettimeofday(&thisSend, NULL); @@ -84,17 +96,30 @@ void Agent::run() { NodeList::getInstance()->sendDomainServerCheckIn(); } - // allow the scripter's call back to setup visual data - emit preSendCallback(); - // flush the voxel packet queue, don't allow it to sleep us - voxelScripter.getVoxelPacketSender()->processWithoutSleep(); + if (!hasVoxelServer) { + for (NodeList::iterator node = nodeList->begin(); node != nodeList->end(); node++) { + if (node->getType() == NODE_TYPE_VOXEL_SERVER) { + hasVoxelServer = true; + } + } + } else { + // allow the scripter's call back to setup visual data + emit willSendVisualDataCallback(); + + if (engine.hasUncaughtException()) { + int line = engine.uncaughtExceptionLineNumber(); + qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; + } + + voxelScripter.getVoxelPacketSender()->processWithoutSleep(); + } while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes)) { NodeList::getInstance()->processNodeData((sockaddr*) &senderAddress, receivedData, receivedBytes); } // sleep for the correct amount of time to have data send be consistently timed - if ((numMicrosecondsSleep = DATA_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) { + if ((numMicrosecondsSleep = VISUAL_DATA_SEND_INTERVAL_USECS - (usecTimestampNow() - usecTimestamp(&thisSend))) > 0) { usleep(numMicrosecondsSleep); } } diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 5e0765fd33..6b5362c3ab 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -23,7 +23,7 @@ public: void run(); signals: - void preSendCallback(); + void willSendVisualDataCallback(); }; #endif /* defined(__hifi__Operative__) */