diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3a213cb64f..11ab5769cb 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -45,6 +45,7 @@ #include "ui/AttachmentsDialog.h" #include "ui/InfoView.h" #include "ui/MetavoxelEditor.h" +#include "ui/MetavoxelNetworkSimulator.h" #include "ui/ModelsBrowser.h" #include "ui/LoginDialog.h" #include "ui/NodeBounds.h" @@ -431,6 +432,8 @@ Menu::Menu() : QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels"); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false, Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData())); + addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this, + SLOT(showMetavoxelNetworkSimulator())); QMenu* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false); @@ -1378,6 +1381,13 @@ void Menu::showMetavoxelEditor() { _MetavoxelEditor->raise(); } +void Menu::showMetavoxelNetworkSimulator() { + if (!_metavoxelNetworkSimulator) { + _metavoxelNetworkSimulator = new MetavoxelNetworkSimulator(); + } + _metavoxelNetworkSimulator->raise(); +} + void Menu::showScriptEditor() { if(!_ScriptEditor || !_ScriptEditor->isVisible()) { _ScriptEditor = new ScriptEditorWindow(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index a1936050ff..80f7f1e006 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -78,6 +78,7 @@ class AttachmentsDialog; class BandwidthDialog; class LodToolsDialog; class MetavoxelEditor; +class MetavoxelNetworkSimulator; class ChatWindow; class OctreeStatsDialog; class MenuItemProperties; @@ -218,6 +219,7 @@ private slots: void cycleFrustumRenderMode(); void runTests(); void showMetavoxelEditor(); + void showMetavoxelNetworkSimulator(); void showScriptEditor(); void showChat(); void toggleConsole(); @@ -274,6 +276,7 @@ private: FrustumDrawMode _frustumDrawMode; ViewFrustumOffset _viewFrustumOffset; QPointer _MetavoxelEditor; + QPointer _metavoxelNetworkSimulator; QPointer _ScriptEditor; QPointer _chatWindow; QDialog* _jsConsole; @@ -430,6 +433,7 @@ namespace MenuOption { const QString MuteEnvironment = "Mute Environment"; const QString MyLocations = "My Locations..."; const QString NameLocation = "Name this location"; + const QString NetworkSimulator = "Network Simulator..."; const QString NewVoxelCullingMode = "New Voxel Culling Mode"; const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity"; const QString OctreeStats = "Voxel and Entity Statistics"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index e9cfde60f9..97385a8671 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -39,6 +39,13 @@ REGISTER_META_OBJECT(StaticModelRenderer) static int bufferPointVectorMetaTypeId = qRegisterMetaType(); +MetavoxelSystem::NetworkSimulation::NetworkSimulation(float dropRate, float repeatRate, int minimumDelay, int maximumDelay) : + dropRate(dropRate), + repeatRate(repeatRate), + minimumDelay(minimumDelay), + maximumDelay(maximumDelay) { +} + void MetavoxelSystem::init() { MetavoxelClientManager::init(); DefaultMetavoxelRendererImplementation::init(); @@ -61,6 +68,16 @@ MetavoxelLOD MetavoxelSystem::getLOD() { return _lod; } +void MetavoxelSystem::setNetworkSimulation(const NetworkSimulation& simulation) { + QWriteLocker locker(&_networkSimulationLock); + _networkSimulation = simulation; +} + +MetavoxelSystem::NetworkSimulation MetavoxelSystem::getNetworkSimulation() { + QReadLocker locker(&_networkSimulationLock); + return _networkSimulation; +} + class SimulateVisitor : public MetavoxelVisitor { public: @@ -692,10 +709,53 @@ MetavoxelData MetavoxelSystemClient::getAugmentedData() { return _augmentedData; } +class ReceiveDelayer : public QObject { +public: + + ReceiveDelayer(const SharedNodePointer& node, const QByteArray& packet); + +protected: + + virtual void timerEvent(QTimerEvent* event); + +private: + + SharedNodePointer _node; + QByteArray _packet; +}; + +ReceiveDelayer::ReceiveDelayer(const SharedNodePointer& node, const QByteArray& packet) : + _node(node), + _packet(packet) { +} + +void ReceiveDelayer::timerEvent(QTimerEvent* event) { + QMutexLocker locker(&_node->getMutex()); + MetavoxelClient* client = static_cast(_node->getLinkedData()); + if (client) { + QMetaObject::invokeMethod(&client->getSequencer(), "receivedDatagram", Q_ARG(const QByteArray&, _packet)); + } + deleteLater(); +} + int MetavoxelSystemClient::parseData(const QByteArray& packet) { // process through sequencer - QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); - Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size()); + MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); + if (randFloat() < simulation.dropRate) { + return packet.size(); + } + int count = (randFloat() < simulation.repeatRate) ? 2 : 1; + for (int i = 0; i < count; i++) { + int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); + if (delay > 0) { + ReceiveDelayer* delayer = new ReceiveDelayer(_node, packet); + delayer->startTimer(delay); + + } else { + QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); + } + Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size()); + } return packet.size(); } @@ -774,9 +834,46 @@ void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) { QThreadPool::globalInstance()->start(new Augmenter(_node, _data, getAugmentedData(), _remoteDataLOD)); } +class SendDelayer : public QObject { +public: + + SendDelayer(const SharedNodePointer& node, const QByteArray& data); + + virtual void timerEvent(QTimerEvent* event); + +private: + + SharedNodePointer _node; + QByteArray _data; +}; + +SendDelayer::SendDelayer(const SharedNodePointer& node, const QByteArray& data) : + _node(node), + _data(data) { +} + +void SendDelayer::timerEvent(QTimerEvent* event) { + NodeList::getInstance()->writeDatagram(_data, _node); + deleteLater(); +} + void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { - NodeList::getInstance()->writeDatagram(data, _node); - Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); + MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); + if (randFloat() < simulation.dropRate) { + return; + } + int count = (randFloat() < simulation.repeatRate) ? 2 : 1; + for (int i = 0; i < count; i++) { + int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); + if (delay > 0) { + SendDelayer* delayer = new SendDelayer(_node, data); + delayer->startTimer(delay); + + } else { + NodeList::getInstance()->writeDatagram(data, _node); + } + Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); + } } BufferData::~BufferData() { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index ac6a75c68b..af6f4c6c7a 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -31,12 +31,25 @@ class MetavoxelSystem : public MetavoxelClientManager { public: + class NetworkSimulation { + public: + float dropRate; + float repeatRate; + int minimumDelay; + int maximumDelay; + + NetworkSimulation(float dropRate = 0.0f, float repeatRate = 0.0f, int minimumDelay = 0, int maximumDelay = 0); + }; + virtual void init(); virtual MetavoxelLOD getLOD(); const Frustum& getFrustum() const { return _frustum; } + void setNetworkSimulation(const NetworkSimulation& simulation); + NetworkSimulation getNetworkSimulation(); + const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; } const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; } const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; } @@ -93,6 +106,9 @@ private: MetavoxelLOD _lod; QReadWriteLock _lodLock; Frustum _frustum; + + NetworkSimulation _networkSimulation; + QReadWriteLock _networkSimulationLock; }; /// Generic abstract base class for objects that handle a signal. diff --git a/libraries/metavoxels/src/Endpoint.h b/libraries/metavoxels/src/Endpoint.h index d6999196d8..7b0adb4cef 100644 --- a/libraries/metavoxels/src/Endpoint.h +++ b/libraries/metavoxels/src/Endpoint.h @@ -32,7 +32,7 @@ public: PacketRecord* baselineReceiveRecord = NULL); virtual ~Endpoint(); - const DatagramSequencer& getSequencer() const { return _sequencer; } + DatagramSequencer& getSequencer() { return _sequencer; } virtual void update();