From ba79b1b329bbe7378cc709b95021dc1baff303c2 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 6 Jan 2014 09:18:10 -0600 Subject: [PATCH 01/58] Using BUILD_VERSION to setApplicationVersion --- interface/resources/info/ApplicationInfo.ini | 3 +-- interface/src/Application.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/resources/info/ApplicationInfo.ini b/interface/resources/info/ApplicationInfo.ini index a1d1603172..35a6c71eb8 100644 --- a/interface/resources/info/ApplicationInfo.ini +++ b/interface/resources/info/ApplicationInfo.ini @@ -1,5 +1,4 @@ [INFO] name=Interface -version=0.0.1 organizationName=High Fidelity -organizationDomain=highfidelity.io \ No newline at end of file +organizationDomain=highfidelity.io diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 64a50a5b99..93af683240 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -190,7 +190,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : applicationInfo.beginGroup("INFO"); setApplicationName(applicationInfo.value("name").toString()); - setApplicationVersion(applicationInfo.value("version").toString()); + setApplicationVersion(BUILD_VERSION); setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); From 314c4ffcfa3c79a12c584f1480f20279bfd17dad Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 6 Jan 2014 09:24:29 -0600 Subject: [PATCH 02/58] Using applicationVersion() instead of const wherever version is specified --- interface/src/Application.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 93af683240..6df74a47d5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -156,8 +156,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // call Menu getInstance static method to set up the menu _window->setMenuBar(Menu::getInstance()); - qDebug("[VERSION] Build sequence: %i\n", BUILD_VERSION); - unsigned int listenPort = 0; // bind to an ephemeral port by default const char** constArgv = const_cast(argv); const char* portStr = getCmdOption(argc, constArgv, "--listenPort"); @@ -193,6 +191,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : setApplicationVersion(BUILD_VERSION); setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); + + qDebug("[VERSION] Build sequence: %s\n", applicationVersion().toStdString().c_str()); _settings = new QSettings(this); @@ -4175,7 +4175,7 @@ void Application::attachNewHeadToNode(Node* newNode) { void Application::updateWindowTitle(){ QString title = ""; - QString buildVersion = " (build " + QString::number(BUILD_VERSION) + ")"; + QString buildVersion = " (build " + applicationVersion() + ")"; QString username = _profile.getUsername(); if(!username.isEmpty()){ title += _profile.getUsername(); From d44126ac33c65bafb270b15c8f29724e23141e40 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 8 Jan 2014 08:30:50 -0600 Subject: [PATCH 03/58] Get version XML and store latest version as application property --- interface/CMakeLists.txt | 3 ++- interface/src/Application.cpp | 41 +++++++++++++++++++++++++++++++++++ interface/src/Application.h | 9 ++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index ecc6ff79cb..ccc26b630a 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -60,6 +60,7 @@ find_package(Qt5OpenGL REQUIRED) find_package(Qt5Svg REQUIRED) find_package(Qt5WebKit REQUIRED) find_package(Qt5WebKitWidgets REQUIRED) +find_package(Qt5Xml REQUIRED) if (APPLE) set(MACOSX_BUNDLE_BUNDLE_NAME Interface) @@ -136,7 +137,7 @@ if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) -qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets) +qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets Xml) # include headers for interface and InterfaceConfig. include_directories( diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6df74a47d5..1cdef9f0fe 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,8 @@ #include #include #include +#include +#include #include #include @@ -192,6 +195,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); + checkVersion(); + qDebug("[VERSION] Build sequence: %s\n", applicationVersion().toStdString().c_str()); _settings = new QSettings(this); @@ -4536,3 +4541,39 @@ void Application::toggleLogDialog() { _logDialog->close(); } } + +void Application::loadLatestVersionDetails() { + QUrl url("https://a-tower.below92.com/version.xml"); + QNetworkAccessManager *downloadXML = new QNetworkAccessManager(this); + QNetworkRequest request(url); + connect(downloadXML, SIGNAL(finished(QNetworkReply*)), this, SLOT(parseVersionXml(QNetworkReply*))); + downloadXML->get(request); + +} + +void Application::parseVersionXml(QNetworkReply *reply) { + QXmlStreamReader xml(reply); + while (!xml.atEnd() && !xml.hasError()) { + QXmlStreamReader::TokenType token = xml.readNext(); + + if (token == QXmlStreamReader::StartElement) { + if (xml.name() == "Version") { + xml.readNext(); + _latestVersion = xml.text().toString(); + qDebug("################ Version found %s\n", _latestVersion.toStdString().c_str()); + } + } + + } +} + +void Application::checkVersion() { + loadLatestVersionDetails(); + + // This is a very rudimentary check, if this version is not equal to latest then you need to get it + // unless you're coming from the future. + + //if (applicationVersion() != 0 && applicationVersion() != this->_latestVersion) { + + //} +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 6f0463bc88..a02f136d3d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -212,6 +212,11 @@ public: /// set a voxel which is to be rendered with a highlight void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; } void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; } + + // Get XML with version information and parse it + // Display dialog when version is not the latest and allow for new version download from link + void loadLatestVersionDetails(); + void checkVersion(); public slots: void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data); @@ -255,6 +260,8 @@ private slots: void restoreMirrorView(); void shrinkMirrorView(); void resetSensors(); + + void parseVersionXml(QNetworkReply *reply); private: void resetCamerasOnResizeGL(Camera& camera, int width, int height); @@ -512,6 +519,8 @@ private: QPointer _logDialog; FileLogger* _logger; + + QString _latestVersion; }; #endif /* defined(__interface__Application__) */ From efb173e1d4d9b4d29815cb0e7b45b6686cc92169 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 8 Jan 2014 21:20:43 -0600 Subject: [PATCH 04/58] Preliminary update dialog work --- interface/resources/styles/update_dialog.qss | 0 interface/src/Application.cpp | 58 +++++++++++++++++--- interface/src/Application.h | 4 +- interface/src/ui/UpdateDialog.cpp | 40 ++++++++++++++ interface/src/ui/UpdateDialog.h | 38 +++++++++++++ 5 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 interface/resources/styles/update_dialog.qss create mode 100644 interface/src/ui/UpdateDialog.cpp create mode 100644 interface/src/ui/UpdateDialog.h diff --git a/interface/resources/styles/update_dialog.qss b/interface/resources/styles/update_dialog.qss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1cdef9f0fe..4a762e4fc3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -195,6 +196,18 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); + #ifdef Q_WS_X11 + _operatingSystem = "ubuntu"; + #endif + + #ifdef Q_WS_WIN + _operatingSystem = "win"; + #endif + + #ifdef Q_WS_MACX + _operatingSystem = "mac"; + #endif + checkVersion(); qDebug("[VERSION] Build sequence: %s\n", applicationVersion().toStdString().c_str()); @@ -1419,6 +1432,7 @@ void Application::idle() { } } } + void Application::terminate() { // Close serial port // close(serial_fd); @@ -4542,7 +4556,7 @@ void Application::toggleLogDialog() { } } -void Application::loadLatestVersionDetails() { +void Application::checkVersion() { QUrl url("https://a-tower.below92.com/version.xml"); QNetworkAccessManager *downloadXML = new QNetworkAccessManager(this); QNetworkRequest request(url); @@ -4552,28 +4566,56 @@ void Application::loadLatestVersionDetails() { } void Application::parseVersionXml(QNetworkReply *reply) { + QString _releaseDate; + QString _releaseNotes; + QString _downloadLink; + QXmlStreamReader xml(reply); while (!xml.atEnd() && !xml.hasError()) { QXmlStreamReader::TokenType token = xml.readNext(); if (token == QXmlStreamReader::StartElement) { + + if (xml.name() == "ReleaseDate") { + xml.readNext(); + _releaseDate = xml.text().toString(); + } + + if (xml.name() == "ReleaseNotes") { + xml.readNext(); + _releaseNotes = xml.text().toString(); + } + if (xml.name() == "Version") { xml.readNext(); _latestVersion = xml.text().toString(); - qDebug("################ Version found %s\n", _latestVersion.toStdString().c_str()); } } } + + if (applicationVersion() != _latestVersion) { + + } + + displayUpdateDialog(); } -void Application::checkVersion() { - loadLatestVersionDetails(); +void Application::displayUpdateDialog() { + int _windowWidth = 500; + int _windowHeigth = 300; + QString _windowTitle = "Newer build available"; - // This is a very rudimentary check, if this version is not equal to latest then you need to get it - // unless you're coming from the future. + QPushButton *download = new QPushButton("Download"); + QPushButton *ignore = new QPushButton("Ignore this version"); + QPushButton *close = new QPushButton("Close"); + + QWidget *updateDialog = new QWidget; + updateDialog->setFixedWidth(_windowWidth); + updateDialog->setFixedHeight(_windowHeigth); + updateDialog->setWindowTitle(_windowTitle); + + updateDialog->show(); - //if (applicationVersion() != 0 && applicationVersion() != this->_latestVersion) { - //} } diff --git a/interface/src/Application.h b/interface/src/Application.h index a02f136d3d..99873d4b57 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -69,6 +69,7 @@ #include "ui/RearMirrorTools.h" #include "ui/LodToolsDialog.h" #include "ui/LogDialog.h" +#include "ui/UpdateDialog.h" #include "FileLogger.h" #include "ParticleTreeRenderer.h" #include "ParticleEditHandle.h" @@ -215,8 +216,8 @@ public: // Get XML with version information and parse it // Display dialog when version is not the latest and allow for new version download from link - void loadLatestVersionDetails(); void checkVersion(); + void displayUpdateDialog(); public slots: void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data); @@ -521,6 +522,7 @@ private: FileLogger* _logger; QString _latestVersion; + QString _operatingSystem; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp new file mode 100644 index 0000000000..c76cad7701 --- /dev/null +++ b/interface/src/ui/UpdateDialog.cpp @@ -0,0 +1,40 @@ +// +// UpdateDialog.cpp +// interface +// +// Created by Leonardo Murillo on 1/8/14. +// Copyright (c) 2013, 2014 High Fidelity, Inc. All rights reserved. +// + +#include +#include +#include + +#include "SharedUtil.h" +#include "UpdateDialog.h" + +const int buttonWidth = 120; +const int buttonHeight = 40; + +const QString dialogTitle = "Update Required"; +const QString updateRequired = QString("You are currently running build %1, the latest build released is %2.\n \ + Please download and install the most recent release to access the latest \ + features and bug fixes.").arg("1", "2"); + +UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString downloadURL) : QDialog(parent, Qt::Dialog) { + setWindowTitle(dialogTitle); + _releaseNotes = new QLabel(this); + _releaseNotes->setText(releaseNotes); + + _updateRequired = new QLabel(this); + _updateRequired->setText(updateRequired); + + _downloadButton = new QPushButton("Download", this); + _downloadButton->setObjectName("downloadButton"); + _skipButton = new QPushButton("Skip Version", this); + _skipButton->setObjectName("skipButton"); + _closeButton = new QPushButton("Close", this); + _closeButton->setObjectName("closeButton"); + + +} \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h new file mode 100644 index 0000000000..9a1bfff1c4 --- /dev/null +++ b/interface/src/ui/UpdateDialog.h @@ -0,0 +1,38 @@ +// +// UpdateDialog.h +// interface +// +// Created by Leonardo Murillo on 1/8/14. +// Copyright (c) 2013, 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __hifi__UpdateDialog__ +#define __hifi__UpdateDialog__ + +#include +#include +#include + +#include + +class UpdateDialog : public QDialog { + Q_OBJECT + +public: + UpdateDialog(QWidget*, QString releaseNotes, QString downloadURL); + ~UpdateDialog(); + +private: + QLabel *_updateRequired; + QLabel *_releaseNotes; + QPushButton *_downloadButton; + QPushButton *_skipButton; + QPushButton *_closeButton; + +private slots: + void handleDownload(); + void handleIgnore(); + void handleClose(); +}; + +#endif /* defined(__hifi__UpdateDialog__) */ From 9dd37344b8a7a65067013e8065cc58db37f1cca2 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 9 Jan 2014 16:14:51 -0600 Subject: [PATCH 05/58] checkpoint --- interface/src/Application.cpp | 23 ++++----------------- interface/src/ui/UpdateDialog.cpp | 33 +++++++++++++++++++++++++++---- interface/src/ui/UpdateDialog.h | 7 ++++--- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4a762e4fc3..0727c8658f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4568,7 +4568,7 @@ void Application::checkVersion() { void Application::parseVersionXml(QNetworkReply *reply) { QString _releaseDate; QString _releaseNotes; - QString _downloadLink; + QUrl *_downloadLink; QXmlStreamReader xml(reply); while (!xml.atEnd() && !xml.hasError()) { @@ -4598,24 +4598,9 @@ void Application::parseVersionXml(QNetworkReply *reply) { } - displayUpdateDialog(); -} - -void Application::displayUpdateDialog() { - int _windowWidth = 500; - int _windowHeigth = 300; - QString _windowTitle = "Newer build available"; - - QPushButton *download = new QPushButton("Download"); - QPushButton *ignore = new QPushButton("Ignore this version"); - QPushButton *close = new QPushButton("Close"); - - QWidget *updateDialog = new QWidget; - updateDialog->setFixedWidth(_windowWidth); - updateDialog->setFixedHeight(_windowHeigth); - updateDialog->setWindowTitle(_windowTitle); - - updateDialog->show(); + _downloadLink = new QUrl("http://www.google.com"); + UpdateDialog *_updateDialog = new UpdateDialog(_glWidget, _releaseNotes, _downloadLink, _latestVersion, applicationVersion()); + _updateDialog->show(); } diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index c76cad7701..d9543f318b 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -15,14 +15,23 @@ const int buttonWidth = 120; const int buttonHeight = 40; +const int dialogWidth = 500; +const int dialogHeigth = 300; const QString dialogTitle = "Update Required"; -const QString updateRequired = QString("You are currently running build %1, the latest build released is %2.\n \ - Please download and install the most recent release to access the latest \ - features and bug fixes.").arg("1", "2"); -UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString downloadURL) : QDialog(parent, Qt::Dialog) { +UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QUrl *downloadURL, QString latestVersion, QString currentVersion) : QDialog(parent, Qt::Dialog) { + + const QString updateRequired = QString("You are currently running build %1, the latest build released is %2.\n \ + Please download and install the most recent release to access the latest \ + features and bug fixes.").arg(currentVersion, latestVersion); + setWindowTitle(dialogTitle); + setModal(true); + QFile styleSheet("resources/styles/update_dialog.qss"); + if (styleSheet.open(QIODevice::ReadOnly)) { + setStyleSheet(styleSheet.readAll()); + } _releaseNotes = new QLabel(this); _releaseNotes->setText(releaseNotes); @@ -36,5 +45,21 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString downlo _closeButton = new QPushButton("Close", this); _closeButton->setObjectName("closeButton"); + _titleBackground = new QFrame(); + connect(_downloadButton, SIGNAL(released()), this, SLOT(handleDownload())); + connect(_skipButton, SIGNAL(released()), this, SLOT(handleDownload())); + connect(_closeButton, SIGNAL(released()), this, SLOT(handleClose())); +} + +void UpdateDialog::handleDownload() { + qDebug("download clicked"); +} + +void UpdateDialog::handleSkip() { + qDebug("skip clicked"); +} + +void UpdateDialog::handleClose() { + qDebug("close clicked"); } \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 9a1bfff1c4..bd374afb5b 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -19,8 +20,7 @@ class UpdateDialog : public QDialog { Q_OBJECT public: - UpdateDialog(QWidget*, QString releaseNotes, QString downloadURL); - ~UpdateDialog(); + UpdateDialog(QWidget*, QString releaseNotes, QUrl *downloadURL, QString latestVersion, QString currentVersion); private: QLabel *_updateRequired; @@ -28,10 +28,11 @@ private: QPushButton *_downloadButton; QPushButton *_skipButton; QPushButton *_closeButton; + QFrame *_titleBackground; private slots: void handleDownload(); - void handleIgnore(); + void handleSkip(); void handleClose(); }; From 664a1a7677eb1402f415eea5e9606d2fdc15636b Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 9 Jan 2014 20:12:39 -0600 Subject: [PATCH 06/58] Checkpoint --- interface/src/Application.cpp | 4 ++-- interface/src/Application.h | 2 +- interface/src/ui/UpdateDialog.cpp | 31 ++++++++++++++++++++++++------- interface/src/ui/UpdateDialog.h | 4 +++- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0727c8658f..b0b26be540 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4588,7 +4588,7 @@ void Application::parseVersionXml(QNetworkReply *reply) { if (xml.name() == "Version") { xml.readNext(); - _latestVersion = xml.text().toString(); + *_latestVersion = xml.text().toString(); } } @@ -4601,6 +4601,6 @@ void Application::parseVersionXml(QNetworkReply *reply) { _downloadLink = new QUrl("http://www.google.com"); UpdateDialog *_updateDialog = new UpdateDialog(_glWidget, _releaseNotes, _downloadLink, _latestVersion, applicationVersion()); - _updateDialog->show(); + _updateDialog->exec(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 99873d4b57..6461bf3925 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -521,7 +521,7 @@ private: FileLogger* _logger; - QString _latestVersion; + QString *_latestVersion; QString _operatingSystem; }; diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index d9543f318b..710c4e7166 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -15,19 +15,27 @@ const int buttonWidth = 120; const int buttonHeight = 40; -const int dialogWidth = 500; +const int buttonMargin = 15; +const int leftStartingPosition = 345; +const int dialogWidth = 750; const int dialogHeigth = 300; const QString dialogTitle = "Update Required"; -UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QUrl *downloadURL, QString latestVersion, QString currentVersion) : QDialog(parent, Qt::Dialog) { +UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QUrl *downloadURL, QString *latestVersion, QString currentVersion) : QDialog(parent, Qt::Dialog) { + + _downloadURL = downloadURL; + _latestVersion = latestVersion; const QString updateRequired = QString("You are currently running build %1, the latest build released is %2.\n \ Please download and install the most recent release to access the latest \ - features and bug fixes.").arg(currentVersion, latestVersion); + features and bug fixes.").arg(currentVersion, *latestVersion); + int leftPosition = leftStartingPosition; setWindowTitle(dialogTitle); + //setWindowFlags(Qt::WindowTitleHint); setModal(true); + resize(dialogWidth, dialogHeigth); QFile styleSheet("resources/styles/update_dialog.qss"); if (styleSheet.open(QIODevice::ReadOnly)) { setStyleSheet(styleSheet.readAll()); @@ -40,26 +48,35 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QUrl *download _downloadButton = new QPushButton("Download", this); _downloadButton->setObjectName("downloadButton"); + _downloadButton->setGeometry(leftPosition, buttonMargin, buttonWidth, buttonHeight); + leftPosition += buttonWidth; + _skipButton = new QPushButton("Skip Version", this); _skipButton->setObjectName("skipButton"); + _skipButton->setGeometry(leftPosition, buttonMargin, buttonWidth, buttonHeight); + leftPosition += buttonWidth; + _closeButton = new QPushButton("Close", this); _closeButton->setObjectName("closeButton"); + _closeButton->setGeometry(leftPosition, buttonMargin, buttonWidth, buttonHeight); _titleBackground = new QFrame(); connect(_downloadButton, SIGNAL(released()), this, SLOT(handleDownload())); - connect(_skipButton, SIGNAL(released()), this, SLOT(handleDownload())); + connect(_skipButton, SIGNAL(released()), this, SLOT(handleSkip())); connect(_closeButton, SIGNAL(released()), this, SLOT(handleClose())); } void UpdateDialog::handleDownload() { - qDebug("download clicked"); + QDesktopServices::openUrl((*_downloadURL)); + close(); } void UpdateDialog::handleSkip() { - qDebug("skip clicked"); + QString fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + fileName.append(QString("/hifi.skipversion")); } void UpdateDialog::handleClose() { - qDebug("close clicked"); + close(); } \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index bd374afb5b..b50b38bf72 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -20,7 +20,7 @@ class UpdateDialog : public QDialog { Q_OBJECT public: - UpdateDialog(QWidget*, QString releaseNotes, QUrl *downloadURL, QString latestVersion, QString currentVersion); + UpdateDialog(QWidget*, QString releaseNotes, QUrl *downloadURL, QString *latestVersion, QString currentVersion); private: QLabel *_updateRequired; @@ -29,6 +29,8 @@ private: QPushButton *_skipButton; QPushButton *_closeButton; QFrame *_titleBackground; + QUrl *_downloadURL; + QString *_latestVersion; private slots: void handleDownload(); From eb52ded4ad4c4a818fb204fa1de66f883dd2efa3 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Thu, 9 Jan 2014 20:34:02 -0600 Subject: [PATCH 07/58] Styling + others --- interface/resources/styles/update_dialog.qss | 6 ++++++ interface/src/Application.cpp | 3 ++- interface/src/ui/UpdateDialog.cpp | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/interface/resources/styles/update_dialog.qss b/interface/resources/styles/update_dialog.qss index e69de29bb2..dbcc2fcbb2 100644 --- a/interface/resources/styles/update_dialog.qss +++ b/interface/resources/styles/update_dialog.qss @@ -0,0 +1,6 @@ +QPushButton { + background-color: #333333; + border-width: 0; + border-top-left-radius: 9px; + border-bottom-left-radius: 9px; +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b0b26be540..207a068232 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4588,7 +4588,8 @@ void Application::parseVersionXml(QNetworkReply *reply) { if (xml.name() == "Version") { xml.readNext(); - *_latestVersion = xml.text().toString(); + QString latestVersion = xml.text().toString(); + _latestVersion = &latestVersion; } } diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 710c4e7166..8185b96e95 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -42,9 +42,11 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QUrl *download } _releaseNotes = new QLabel(this); _releaseNotes->setText(releaseNotes); + _releaseNotes->setObjectName("releaseNotes"); _updateRequired = new QLabel(this); _updateRequired->setText(updateRequired); + _updateRequired->setObjectName("updateRequired"); _downloadButton = new QPushButton("Download", this); _downloadButton->setObjectName("downloadButton"); From ede32cfadc08d7c9a2d12b7d9bf4f3a5470b3b2d Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 10 Jan 2014 14:08:10 -0600 Subject: [PATCH 08/58] Checkpoint --- interface/resources/styles/update_dialog.qss | 12 +++++-- interface/src/Application.cpp | 34 +++++++++++++++++--- interface/src/Application.h | 9 ++++-- interface/src/ui/UpdateDialog.cpp | 22 +++++++------ interface/src/ui/UpdateDialog.h | 3 +- 5 files changed, 58 insertions(+), 22 deletions(-) diff --git a/interface/resources/styles/update_dialog.qss b/interface/resources/styles/update_dialog.qss index dbcc2fcbb2..005ab72b47 100644 --- a/interface/resources/styles/update_dialog.qss +++ b/interface/resources/styles/update_dialog.qss @@ -1,6 +1,12 @@ QPushButton { background-color: #333333; border-width: 0; - border-top-left-radius: 9px; - border-bottom-left-radius: 9px; -} + border-radius: 9px; + border-radius: 9px; + font-family: Arial; + font-size: 18px; + font-weight: 100; + color: #b7b7b7; + width: 120px; + height: 40px; +} \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 207a068232..fe37de1c4f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4588,20 +4588,44 @@ void Application::parseVersionXml(QNetworkReply *reply) { if (xml.name() == "Version") { xml.readNext(); - QString latestVersion = xml.text().toString(); - _latestVersion = &latestVersion; + _latestVersion = new QString(xml.text().toString()); } } } - - if (applicationVersion() != _latestVersion) { + if (!shouldSkipVersion() && applicationVersion() != _latestVersion) { } _downloadLink = new QUrl("http://www.google.com"); - UpdateDialog *_updateDialog = new UpdateDialog(_glWidget, _releaseNotes, _downloadLink, _latestVersion, applicationVersion()); + UpdateDialog *_updateDialog = new UpdateDialog(_glWidget, _releaseNotes, _downloadLink); _updateDialog->exec(); } + +QFile *Application::loadSkipFile() { + QString fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + fileName.append(QString("/hifi.skipversion")); + QFile *file = new QFile(fileName); + file->open(QIODevice::ReadWrite); + return file; +} + +bool Application::shouldSkipVersion() { + QFile *skipFile = loadSkipFile(); + QByteArray skipFileContents = skipFile->readAll(); + QString *skipVersion = new QString(skipFileContents); + skipFile->close(); + if (*skipVersion == *_latestVersion ) { + return true; + } + return false; +} + +void Application::skipVersion() { + QFile *skipFile = loadSkipFile(); + skipFile->seek(0); + skipFile->write(_latestVersion->toStdString().c_str()); + skipFile->close(); +} diff --git a/interface/src/Application.h b/interface/src/Application.h index 6461bf3925..88b38ddbbb 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -216,8 +216,14 @@ public: // Get XML with version information and parse it // Display dialog when version is not the latest and allow for new version download from link + QFile *loadSkipFile(); void checkVersion(); void displayUpdateDialog(); + bool shouldSkipVersion(); + void skipVersion(); + + QString *_latestVersion; + QString *_operatingSystem; public slots: void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data); @@ -520,9 +526,6 @@ private: QPointer _logDialog; FileLogger* _logger; - - QString *_latestVersion; - QString _operatingSystem; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 8185b96e95..d8bf6bd742 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -6,30 +6,32 @@ // Copyright (c) 2013, 2014 High Fidelity, Inc. All rights reserved. // +#include #include #include #include +#include "Application.h" #include "SharedUtil.h" #include "UpdateDialog.h" -const int buttonWidth = 120; +const int buttonWidth = 125; const int buttonHeight = 40; -const int buttonMargin = 15; -const int leftStartingPosition = 345; +const int buttonMargin = 100; +const int leftStartingPosition = 275; const int dialogWidth = 750; const int dialogHeigth = 300; const QString dialogTitle = "Update Required"; -UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QUrl *downloadURL, QString *latestVersion, QString currentVersion) : QDialog(parent, Qt::Dialog) { +UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QUrl *downloadURL) : QDialog(parent, Qt::Dialog) { + Application* application = Application::getInstance(); _downloadURL = downloadURL; - _latestVersion = latestVersion; const QString updateRequired = QString("You are currently running build %1, the latest build released is %2.\n \ Please download and install the most recent release to access the latest \ - features and bug fixes.").arg(currentVersion, *latestVersion); + features and bug fixes.").arg(application->applicationVersion(), *application->_latestVersion); int leftPosition = leftStartingPosition; setWindowTitle(dialogTitle); @@ -70,13 +72,15 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QUrl *download } void UpdateDialog::handleDownload() { + Application* application = Application::getInstance(); QDesktopServices::openUrl((*_downloadURL)); - close(); + application->quit(); } void UpdateDialog::handleSkip() { - QString fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - fileName.append(QString("/hifi.skipversion")); + Application* application = Application::getInstance(); + application->skipVersion(); + close(); } void UpdateDialog::handleClose() { diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index b50b38bf72..54ce812471 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -20,7 +20,7 @@ class UpdateDialog : public QDialog { Q_OBJECT public: - UpdateDialog(QWidget*, QString releaseNotes, QUrl *downloadURL, QString *latestVersion, QString currentVersion); + UpdateDialog(QWidget*, QString releaseNotes, QUrl *downloadURL); private: QLabel *_updateRequired; @@ -30,7 +30,6 @@ private: QPushButton *_closeButton; QFrame *_titleBackground; QUrl *_downloadURL; - QString *_latestVersion; private slots: void handleDownload(); From 1c2b27f0f40ed3a71c0fdf9387584b299653a459 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 10 Jan 2014 15:18:24 -0600 Subject: [PATCH 09/58] Checkpoint --- interface/CMakeLists.txt | 2 +- interface/src/Application.cpp | 26 +++++++++++--------------- interface/src/Application.h | 1 + interface/src/ui/UpdateDialog.cpp | 5 ++--- interface/src/ui/UpdateDialog.h | 2 +- 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 3cf3463f83..ee9188a7d7 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -19,7 +19,7 @@ set(UVCCAMERACONTROL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/UVCCameraCont if (DEFINED ENV{JOB_ID}) set(BUILD_SEQ $ENV{JOB_ID}) else () - set(BUILD_SEQ "0") + set(BUILD_SEQ "0.1") endif () set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} $ENV{QT_CMAKE_PREFIX_PATH}) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 79a32addb2..4e9705b49c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -89,6 +89,8 @@ const float MIRROR_FULLSCREEN_DISTANCE = 0.35f; const float MIRROR_REARVIEW_DISTANCE = 0.65f; const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; +const QString CHECK_VERSION_URL = "http://www.google.com"; + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); @@ -196,20 +198,21 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); #ifdef Q_WS_X11 - _operatingSystem = "ubuntu"; + _operatingSystem = new QString("ubuntu"); #endif #ifdef Q_WS_WIN - _operatingSystem = "win"; + _operatingSystem = new QString("win"); #endif #ifdef Q_WS_MACX - _operatingSystem = "mac"; + _operatingSystem = new QString("mac"); #endif checkVersion(); qDebug("[VERSION] Build sequence: %s\n", applicationVersion().toStdString().c_str()); + qDebug("[OS] %s", _operatingSystem->toStdString().c_str()); _settings = new QSettings(this); @@ -4555,40 +4558,33 @@ void Application::checkVersion() { void Application::parseVersionXml(QNetworkReply *reply) { QString _releaseDate; QString _releaseNotes; - QUrl *_downloadLink; QXmlStreamReader xml(reply); while (!xml.atEnd() && !xml.hasError()) { QXmlStreamReader::TokenType token = xml.readNext(); if (token == QXmlStreamReader::StartElement) { - if (xml.name() == "ReleaseDate") { xml.readNext(); _releaseDate = xml.text().toString(); } - if (xml.name() == "ReleaseNotes") { xml.readNext(); _releaseNotes = xml.text().toString(); } - if (xml.name() == "Version") { xml.readNext(); _latestVersion = new QString(xml.text().toString()); } } - } + + _downloadURL = new QUrl(CHECK_VERSION_URL); + if (!shouldSkipVersion() && applicationVersion() != _latestVersion) { - + UpdateDialog *_updateDialog = new UpdateDialog(_glWidget, _releaseNotes); + _updateDialog->exec(); } - - _downloadLink = new QUrl("http://www.google.com"); - - UpdateDialog *_updateDialog = new UpdateDialog(_glWidget, _releaseNotes, _downloadLink); - _updateDialog->exec(); - } QFile *Application::loadSkipFile() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 88b38ddbbb..8d45b9257f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -224,6 +224,7 @@ public: QString *_latestVersion; QString *_operatingSystem; + QUrl *_downloadURL; public slots: void sendAvatarFaceVideoMessage(int frameCount, const QByteArray& data); diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index d8bf6bd742..c218d9ddbd 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -24,10 +24,9 @@ const int dialogHeigth = 300; const QString dialogTitle = "Update Required"; -UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QUrl *downloadURL) : QDialog(parent, Qt::Dialog) { +UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes) : QDialog(parent, Qt::Dialog) { Application* application = Application::getInstance(); - _downloadURL = downloadURL; const QString updateRequired = QString("You are currently running build %1, the latest build released is %2.\n \ Please download and install the most recent release to access the latest \ @@ -73,7 +72,7 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QUrl *download void UpdateDialog::handleDownload() { Application* application = Application::getInstance(); - QDesktopServices::openUrl((*_downloadURL)); + QDesktopServices::openUrl(*application->_downloadURL); application->quit(); } diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 54ce812471..4f5969f3bd 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -20,7 +20,7 @@ class UpdateDialog : public QDialog { Q_OBJECT public: - UpdateDialog(QWidget*, QString releaseNotes, QUrl *downloadURL); + UpdateDialog(QWidget*, QString releaseNotes); private: QLabel *_updateRequired; From ca16acef6be5d01dd045566be9c0ac6c0d69f96a Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 13 Jan 2014 07:37:47 -0600 Subject: [PATCH 10/58] Checkpoint --- interface/InterfaceVersion.h.in | 2 +- interface/src/Application.cpp | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/interface/InterfaceVersion.h.in b/interface/InterfaceVersion.h.in index 26b2260965..60e51b4ab6 100644 --- a/interface/InterfaceVersion.h.in +++ b/interface/InterfaceVersion.h.in @@ -6,4 +6,4 @@ // Copyright (c) 2013 High Fidelity, Inc.. All rights reserved. // -const int BUILD_VERSION = @BUILD_SEQ@; +const QString BUILD_VERSION = "@BUILD_SEQ@"; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4e9705b49c..1f433868a0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -197,22 +197,17 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); - #ifdef Q_WS_X11 - _operatingSystem = new QString("ubuntu"); - #endif - - #ifdef Q_WS_WIN + #ifdef Q_OS_WIN32 _operatingSystem = new QString("win"); #endif - #ifdef Q_WS_MACX + #ifdef Q_OS_MAC _operatingSystem = new QString("mac"); #endif checkVersion(); qDebug("[VERSION] Build sequence: %s\n", applicationVersion().toStdString().c_str()); - qDebug("[OS] %s", _operatingSystem->toStdString().c_str()); _settings = new QSettings(this); From 4373cc17189b90c35993a1e8306b1fd96d2f8ad5 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 13 Jan 2014 11:05:58 -0600 Subject: [PATCH 11/58] Trivial commit - triggering github merge for testing --- interface/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index ee9188a7d7..7d71cd11fe 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -228,3 +228,4 @@ INSTALL(TARGETS ${TARGET_NAME} BUNDLE DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/install COMPONENT Runtime RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/install COMPONENT Runtime ) + From 4a4b4835321f7c710e23da9486e0627f9c1ba257 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 14 Jan 2014 10:31:47 -0600 Subject: [PATCH 12/58] Resolving conflicts --- interface/src/Application.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6bb54e7d95..d50b8962a9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -89,12 +89,8 @@ const float MIRROR_FULLSCREEN_DISTANCE = 0.35f; const float MIRROR_REARVIEW_DISTANCE = 0.65f; const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; -<<<<<<< HEAD const QString CHECK_VERSION_URL = "http://www.google.com"; - -======= ->>>>>>> a8bbe41642f5ce3152f7e7ca8b9b001c78c2fcdc void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); Application::getInstance()->getLogger()->addMessage(message.toLocal8Bit().constData()); From f9bb98680deacec117d7f2a85029b87a5d142336 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 14 Jan 2014 21:57:28 -0600 Subject: [PATCH 13/58] Using Qt Designer .ui file instead of manually coding dialog --- interface/CMakeLists.txt | 3 +- interface/resources/ui/updateDialog.ui | 182 +++++++++++++++++++++++++ interface/src/Application.cpp | 13 +- interface/src/ui/UpdateDialog.cpp | 54 +++----- interface/src/ui/UpdateDialog.h | 9 -- 5 files changed, 210 insertions(+), 51 deletions(-) create mode 100644 interface/resources/ui/updateDialog.ui diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 7d71cd11fe..ab68326765 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -60,6 +60,7 @@ find_package(Qt5Svg REQUIRED) find_package(Qt5WebKit REQUIRED) find_package(Qt5WebKitWidgets REQUIRED) find_package(Qt5Xml REQUIRED) +find_package(Qt5UiTools REQUIRED) if (APPLE) set(MACOSX_BUNDLE_BUNDLE_NAME Interface) @@ -135,7 +136,7 @@ if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) target_link_libraries(${TARGET_NAME} ${LIBOVR_LIBRARIES}) endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) -qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets Xml) +qt5_use_modules(${TARGET_NAME} Core Gui Multimedia Network OpenGL Script Svg WebKit WebKitWidgets Xml UiTools) # include headers for interface and InterfaceConfig. include_directories( diff --git a/interface/resources/ui/updateDialog.ui b/interface/resources/ui/updateDialog.ui new file mode 100644 index 0000000000..de3358ea80 --- /dev/null +++ b/interface/resources/ui/updateDialog.ui @@ -0,0 +1,182 @@ + + + Dialog + + + Qt::NonModal + + + + 0 + 0 + 750 + 300 + + + + PointingHandCursor + + + Update Required + + + background-color: rgb(255, 255, 255); + + + + + 0 + 0 + 751 + 71 + + + + Qt::LeftToRight + + + false + + + background-color: rgb(236, 236, 236); + + + QFrame::StyledPanel + + + QFrame::Plain + + + 0 + + + + + 240 + 10 + 271 + 41 + + + + + Arial + 36 + PreferAntialias + false + + + + color: rgb(51, 51, 51); + + + Update Required + + + + + + + 100 + 110 + 561 + 61 + + + + + Arial + 18 + 50 + false + + + + You are currently running build 312, the latest build available is 375. Please download and install the most recent release to access the latest features and bug fixes. + + + true + + + + + + 360 + 240 + 364 + 40 + + + + + + + PointingHandCursor + + + background-color: #333333; + border-width: 0; + border-radius: 9px; + border-radius: 9px; + font-family: Arial; + font-size: 18px; + font-weight: 100; + color: #b7b7b7; + width: 120px; + height: 40px; + + + Download + + + + + + + PointingHandCursor + + + background-color: #333333; + border-width: 0; + border-radius: 9px; + border-radius: 9px; + font-family: Arial; + font-size: 18px; + font-weight: 100; + color: #b7b7b7; + width: 120px; + height: 40px; + + + Skip Version + + + + + + + PointingHandCursor + + + background-color: #333333; + border-width: 0; + border-radius: 9px; + border-radius: 9px; + font-family: Arial; + font-size: 18px; + font-weight: 100; + color: #b7b7b7; + width: 120px; + height: 40px; + + + Close + + + + + + + + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d50b8962a9..6cab83a44b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -89,7 +89,7 @@ const float MIRROR_FULLSCREEN_DISTANCE = 0.35f; const float MIRROR_REARVIEW_DISTANCE = 0.65f; const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; -const QString CHECK_VERSION_URL = "http://www.google.com"; +const QString CHECK_VERSION_URL = "http://highfidelity.io/latestVersion.xml"; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { fprintf(stdout, "%s", message.toLocal8Bit().constData()); @@ -4602,7 +4602,7 @@ void Application::updateLocalOctreeCache(bool firstTime) { } void Application::checkVersion() { - QUrl url("https://a-tower.below92.com/version.xml"); + QUrl url(CHECK_VERSION_URL); QNetworkAccessManager *downloadXML = new QNetworkAccessManager(this); QNetworkRequest request(url); connect(downloadXML, SIGNAL(finished(QNetworkReply*)), this, SLOT(parseVersionXml(QNetworkReply*))); @@ -4631,14 +4631,15 @@ void Application::parseVersionXml(QNetworkReply *reply) { xml.readNext(); _latestVersion = new QString(xml.text().toString()); } + if (xml.name() == _operatingSystem) { + xml.readNext(); + _downloadURL = new QUrl(xml.text().toString()); + } } } - _downloadURL = new QUrl(CHECK_VERSION_URL); - if (!shouldSkipVersion() && applicationVersion() != _latestVersion) { UpdateDialog *_updateDialog = new UpdateDialog(_glWidget, _releaseNotes); - _updateDialog->exec(); } } @@ -4655,7 +4656,7 @@ bool Application::shouldSkipVersion() { QByteArray skipFileContents = skipFile->readAll(); QString *skipVersion = new QString(skipFileContents); skipFile->close(); - if (*skipVersion == *_latestVersion ) { + if (*skipVersion == *_latestVersion /*|| applicationVersion() == "0.1"*/) { return true; } return false; diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index c218d9ddbd..5e924221ea 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "Application.h" #include "SharedUtil.h" @@ -26,44 +27,26 @@ const QString dialogTitle = "Update Required"; UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes) : QDialog(parent, Qt::Dialog) { + QUiLoader updateDialogLoader; + + QFile updateDialogUi("resources/ui/updateDialog.ui"); + updateDialogUi.open(QFile::ReadOnly); + QWidget *updateDialog = updateDialogLoader.load(&updateDialogUi, this); + updateDialogUi.close(); + + updateDialog->show(); + Application* application = Application::getInstance(); - const QString updateRequired = QString("You are currently running build %1, the latest build released is %2.\n \ - Please download and install the most recent release to access the latest \ - features and bug fixes.").arg(application->applicationVersion(), *application->_latestVersion); + const QString updateRequired = QString("You are currently running build %1, the latest build released is %2. Please download and install the most recent release to access the latest features and bug fixes.").arg(application->applicationVersion(), *application->_latestVersion); - int leftPosition = leftStartingPosition; - setWindowTitle(dialogTitle); - //setWindowFlags(Qt::WindowTitleHint); - setModal(true); - resize(dialogWidth, dialogHeigth); - QFile styleSheet("resources/styles/update_dialog.qss"); - if (styleSheet.open(QIODevice::ReadOnly)) { - setStyleSheet(styleSheet.readAll()); - } - _releaseNotes = new QLabel(this); - _releaseNotes->setText(releaseNotes); - _releaseNotes->setObjectName("releaseNotes"); - _updateRequired = new QLabel(this); - _updateRequired->setText(updateRequired); - _updateRequired->setObjectName("updateRequired"); + QPushButton *_downloadButton = updateDialog->findChild("downloadButton"); + QPushButton *_skipButton = updateDialog->findChild("skipButton"); + QPushButton *_closeButton = updateDialog->findChild("closeButton"); + QLabel *_updateContent = updateDialog->findChild("updateContent"); - _downloadButton = new QPushButton("Download", this); - _downloadButton->setObjectName("downloadButton"); - _downloadButton->setGeometry(leftPosition, buttonMargin, buttonWidth, buttonHeight); - leftPosition += buttonWidth; - - _skipButton = new QPushButton("Skip Version", this); - _skipButton->setObjectName("skipButton"); - _skipButton->setGeometry(leftPosition, buttonMargin, buttonWidth, buttonHeight); - leftPosition += buttonWidth; - - _closeButton = new QPushButton("Close", this); - _closeButton->setObjectName("closeButton"); - _closeButton->setGeometry(leftPosition, buttonMargin, buttonWidth, buttonHeight); - - _titleBackground = new QFrame(); + _updateContent->setText(updateRequired); connect(_downloadButton, SIGNAL(released()), this, SLOT(handleDownload())); connect(_skipButton, SIGNAL(released()), this, SLOT(handleSkip())); @@ -79,9 +62,10 @@ void UpdateDialog::handleDownload() { void UpdateDialog::handleSkip() { Application* application = Application::getInstance(); application->skipVersion(); - close(); + this->QDialog::close(); } void UpdateDialog::handleClose() { - close(); + qDebug("###### HANDLECLOSE\n"); + this->QDialog::close(); } \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 4f5969f3bd..425a4e5dc7 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -22,15 +22,6 @@ class UpdateDialog : public QDialog { public: UpdateDialog(QWidget*, QString releaseNotes); -private: - QLabel *_updateRequired; - QLabel *_releaseNotes; - QPushButton *_downloadButton; - QPushButton *_skipButton; - QPushButton *_closeButton; - QFrame *_titleBackground; - QUrl *_downloadURL; - private slots: void handleDownload(); void handleSkip(); From b20334de1d63fb7c1d82dcdeb8b5a7600197a72a Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 14 Jan 2014 22:13:49 -0600 Subject: [PATCH 14/58] Dont need qss --- interface/resources/styles/update_dialog.qss | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 interface/resources/styles/update_dialog.qss diff --git a/interface/resources/styles/update_dialog.qss b/interface/resources/styles/update_dialog.qss deleted file mode 100644 index 005ab72b47..0000000000 --- a/interface/resources/styles/update_dialog.qss +++ /dev/null @@ -1,12 +0,0 @@ -QPushButton { - background-color: #333333; - border-width: 0; - border-radius: 9px; - border-radius: 9px; - font-family: Arial; - font-size: 18px; - font-weight: 100; - color: #b7b7b7; - width: 120px; - height: 40px; -} \ No newline at end of file From 40492a1713fd55d6b17e6761d4e4392b71baacd1 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 14 Jan 2014 22:16:06 -0600 Subject: [PATCH 15/58] Removing text from .ui --- interface/resources/ui/updateDialog.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/ui/updateDialog.ui b/interface/resources/ui/updateDialog.ui index de3358ea80..b0f1e63cb9 100644 --- a/interface/resources/ui/updateDialog.ui +++ b/interface/resources/ui/updateDialog.ui @@ -92,13 +92,13 @@ - You are currently running build 312, the latest build available is 375. Please download and install the most recent release to access the latest features and bug fixes. + true - + 360 From 049be6a56953d656755b58a72333b5cff376d8c2 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 15 Jan 2014 12:40:40 -0600 Subject: [PATCH 16/58] Finishing changes --- interface/src/Application.cpp | 4 +++- interface/src/Application.h | 1 + interface/src/ui/UpdateDialog.cpp | 34 +++++++++++++++++++++---------- interface/src/ui/UpdateDialog.h | 6 ++++++ 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bbc61234de..609214df5a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include @@ -4456,7 +4458,7 @@ void Application::parseVersionXml(QNetworkReply *reply) { } if (!shouldSkipVersion() && applicationVersion() != _latestVersion) { - UpdateDialog *_updateDialog = new UpdateDialog(_glWidget, _releaseNotes); + _updateDialog = new UpdateDialog(_glWidget, _releaseNotes); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 5f705b22f3..5a06c69808 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -220,6 +220,7 @@ public: QString *_latestVersion; QString *_operatingSystem; QUrl *_downloadURL; + QWidget *_updateDialog; public slots: void domainChanged(const QString& domainHostname); diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 326957638d..d3aefebc97 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -26,33 +26,45 @@ const int dialogHeigth = 300; const QString dialogTitle = "Update Required"; UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes) : QDialog(parent, Qt::Dialog) { + Application* application = Application::getInstance(); QUiLoader updateDialogLoader; QFile updateDialogUi("resources/ui/updateDialog.ui"); updateDialogUi.open(QFile::ReadOnly); - QWidget *updateDialog = updateDialogLoader.load(&updateDialogUi, this); + dialogWidget = updateDialogLoader.load(&updateDialogUi, parent); updateDialogUi.close(); - updateDialog->show(); - - Application* application = Application::getInstance(); - const QString updateRequired = QString("You are currently running build %1, the latest build released is %2. \ Please download and install the most recent release to access the latest features and bug fixes.") .arg(application->applicationVersion(), *application->_latestVersion); - QPushButton *_downloadButton = updateDialog->findChild("downloadButton"); - QPushButton *_skipButton = updateDialog->findChild("skipButton"); - QPushButton *_closeButton = updateDialog->findChild("closeButton"); - QLabel *_updateContent = updateDialog->findChild("updateContent"); + setAttribute(Qt::WA_DeleteOnClose); + + QPushButton *_downloadButton = dialogWidget->findChild("downloadButton"); + QPushButton *_skipButton = dialogWidget->findChild("skipButton"); + QPushButton *_closeButton = dialogWidget->findChild("closeButton"); + QLabel *_updateContent = dialogWidget->findChild("updateContent"); _updateContent->setText(updateRequired); connect(_downloadButton, SIGNAL(released()), this, SLOT(handleDownload())); connect(_skipButton, SIGNAL(released()), this, SLOT(handleSkip())); connect(_closeButton, SIGNAL(released()), this, SLOT(handleClose())); + dialogWidget->show(); +} + +UpdateDialog::~UpdateDialog() { + deleteLater(); +} + +void UpdateDialog::toggleUpdateDialog() { + if (this->dialogWidget->isVisible()) { + this->dialogWidget->hide(); + } else { + this->dialogWidget->show(); + } } void UpdateDialog::handleDownload() { @@ -64,9 +76,9 @@ void UpdateDialog::handleDownload() { void UpdateDialog::handleSkip() { Application* application = Application::getInstance(); application->skipVersion(); - this->reject(); + this->toggleUpdateDialog(); } void UpdateDialog::handleClose() { - this->reject(); + this->toggleUpdateDialog(); } \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 425a4e5dc7..50ab7dacbe 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -21,6 +22,11 @@ class UpdateDialog : public QDialog { public: UpdateDialog(QWidget*, QString releaseNotes); + ~UpdateDialog(); + void toggleUpdateDialog(); + +private: + QWidget *dialogWidget; private slots: void handleDownload(); From dd2392f74bbc00d2c5adcb3044b841b377296e08 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 15 Jan 2014 12:40:56 -0600 Subject: [PATCH 17/58] Finishing changes --- interface/src/Application.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 609214df5a..49e6837d15 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -41,8 +41,6 @@ #include #include #include -#include -#include #include #include From f187ac0f98dd1aa61caed834fecaa6ad97b1781e Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 15 Jan 2014 12:44:22 -0600 Subject: [PATCH 18/58] Removing comment --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 49e6837d15..bf70430d24 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4473,7 +4473,7 @@ bool Application::shouldSkipVersion() { QByteArray skipFileContents = skipFile->readAll(); QString *skipVersion = new QString(skipFileContents); skipFile->close(); - if (*skipVersion == *_latestVersion /*|| applicationVersion() == "0.1"*/) { + if (*skipVersion == *_latestVersion || applicationVersion() == "0.1") { return true; } return false; From f975c8ff6948044b1e95d07e45fecc33c72e06eb Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 15 Jan 2014 12:45:26 -0600 Subject: [PATCH 19/58] Removing unneeded vars --- interface/src/ui/UpdateDialog.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index d3aefebc97..5096c462d3 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -16,15 +16,6 @@ #include "SharedUtil.h" #include "UpdateDialog.h" -const int buttonWidth = 125; -const int buttonHeight = 40; -const int buttonMargin = 100; -const int leftStartingPosition = 275; -const int dialogWidth = 750; -const int dialogHeigth = 300; - -const QString dialogTitle = "Update Required"; - UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes) : QDialog(parent, Qt::Dialog) { Application* application = Application::getInstance(); From 1b687848d1a3d43441db640b1424f1bcea504289 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 15 Jan 2014 12:47:19 -0600 Subject: [PATCH 20/58] Unneeded include --- interface/src/Application.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bf70430d24..82fe11d174 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include #include From 746b45d9391e34cacc1c684839e98ee31a0b3d86 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Wed, 15 Jan 2014 15:08:47 -0600 Subject: [PATCH 21/58] Moving ifdef to xml --- interface/src/Application.cpp | 18 ++++++++++-------- interface/src/Application.h | 1 - 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f264b9ba9b..e1e0446ef4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -197,14 +197,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); - #ifdef Q_OS_WIN32 - _operatingSystem = new QString("win"); - #endif - - #ifdef Q_OS_MAC - _operatingSystem = new QString("mac"); - #endif - checkVersion(); qDebug("[VERSION] Build sequence: %s\n", applicationVersion().toStdString().c_str()); @@ -4430,6 +4422,16 @@ void Application::checkVersion() { } void Application::parseVersionXml(QNetworkReply *reply) { + QString *_operatingSystem; + + #ifdef Q_OS_WIN32 + _operatingSystem = new QString("win"); + #endif + + #ifdef Q_OS_MAC + _operatingSystem = new QString("mac"); + #endif + QString _releaseDate; QString _releaseNotes; diff --git a/interface/src/Application.h b/interface/src/Application.h index 5a06c69808..d9ea3c8ae3 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -218,7 +218,6 @@ public: void skipVersion(); QString *_latestVersion; - QString *_operatingSystem; QUrl *_downloadURL; QWidget *_updateDialog; From b8aaf4e813d1e77622e15cbaadbfa6f78518b5a6 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 17 Jan 2014 13:06:54 -0600 Subject: [PATCH 22/58] CR fixes --- interface/src/Application.cpp | 36 ++++++++++++++------------ interface/src/Application.h | 22 ++++++---------- interface/src/ui/UpdateDialog.cpp | 42 +++++++++++-------------------- interface/src/ui/UpdateDialog.h | 5 ++-- 4 files changed, 44 insertions(+), 61 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e1e0446ef4..f8621f03c6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4422,18 +4422,22 @@ void Application::checkVersion() { } void Application::parseVersionXml(QNetworkReply *reply) { - QString *_operatingSystem; + QString *operatingSystem; #ifdef Q_OS_WIN32 - _operatingSystem = new QString("win"); + operatingSystem = new QString("win"); #endif #ifdef Q_OS_MAC - _operatingSystem = new QString("mac"); + operatingSystem = new QString("mac"); #endif - QString _releaseDate; - QString _releaseNotes; + QString releaseDate; + QString releaseNotes; + QString latestVersion; + QUrl downloadURL; + + QWidget *updateDialog; QXmlStreamReader xml(reply); while (!xml.atEnd() && !xml.hasError()) { @@ -4442,25 +4446,25 @@ void Application::parseVersionXml(QNetworkReply *reply) { if (token == QXmlStreamReader::StartElement) { if (xml.name() == "ReleaseDate") { xml.readNext(); - _releaseDate = xml.text().toString(); + releaseDate = xml.text().toString(); } if (xml.name() == "ReleaseNotes") { xml.readNext(); - _releaseNotes = xml.text().toString(); + releaseNotes = xml.text().toString(); } if (xml.name() == "Version") { xml.readNext(); - _latestVersion = new QString(xml.text().toString()); + latestVersion = xml.text().toString(); } - if (xml.name() == _operatingSystem) { + if (xml.name() == operatingSystem) { xml.readNext(); - _downloadURL = new QUrl(xml.text().toString()); + downloadURL = QUrl(xml.text().toString()); } } } - if (!shouldSkipVersion() && applicationVersion() != _latestVersion) { - _updateDialog = new UpdateDialog(_glWidget, _releaseNotes); + if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) { + updateDialog = new UpdateDialog(_glWidget, releaseNotes); } } @@ -4472,20 +4476,20 @@ QFile *Application::loadSkipFile() { return file; } -bool Application::shouldSkipVersion() { +bool Application::shouldSkipVersion(QString latestVersion) { QFile *skipFile = loadSkipFile(); QByteArray skipFileContents = skipFile->readAll(); QString *skipVersion = new QString(skipFileContents); skipFile->close(); - if (*skipVersion == *_latestVersion || applicationVersion() == "0.1") { + if (*skipVersion == latestVersion || applicationVersion() == "0.1") { return true; } return false; } -void Application::skipVersion() { +void Application::skipVersion(QString latestVersion) { QFile *skipFile = loadSkipFile(); skipFile->seek(0); - skipFile->write(_latestVersion->toStdString().c_str()); + skipFile->write(latestVersion.toStdString().c_str()); skipFile->close(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index d9ea3c8ae3..3b5276f891 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -208,18 +208,6 @@ public: /// set a voxel which is to be rendered with a highlight void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; } void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; } - - // Get XML with version information and parse it - // Display dialog when version is not the latest and allow for new version download from link - QFile *loadSkipFile(); - void checkVersion(); - void displayUpdateDialog(); - bool shouldSkipVersion(); - void skipVersion(); - - QString *_latestVersion; - QUrl *_downloadURL; - QWidget *_updateDialog; public slots: void domainChanged(const QString& domainHostname); @@ -267,8 +255,6 @@ private slots: void restoreMirrorView(); void shrinkMirrorView(); void resetSensors(); - - void parseVersionXml(QNetworkReply *reply); private: void resetCamerasOnResizeGL(Camera& camera, int width, int height); @@ -522,6 +508,14 @@ private: QString getLocalVoxelCacheFileName(); void updateLocalOctreeCache(bool firstTime = false); + + QFile *loadSkipFile(); + void checkVersion(); + void displayUpdateDialog(); + bool shouldSkipVersion(QString latestVersion); + void skipVersion(QString latestVersion); + + void parseVersionXml(QNetworkReply *reply); }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 5096c462d3..256183682b 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -16,7 +16,7 @@ #include "SharedUtil.h" #include "UpdateDialog.h" -UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes) : QDialog(parent, Qt::Dialog) { +UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latestVersion, QUrl downloadURL) : QDialog(parent, Qt::Dialog) { Application* application = Application::getInstance(); QUiLoader updateDialogLoader; @@ -28,48 +28,34 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes) : QDialog(pare const QString updateRequired = QString("You are currently running build %1, the latest build released is %2. \ Please download and install the most recent release to access the latest features and bug fixes.") - .arg(application->applicationVersion(), *application->_latestVersion); + .arg(application->applicationVersion(), latestVersion); setAttribute(Qt::WA_DeleteOnClose); - QPushButton *_downloadButton = dialogWidget->findChild("downloadButton"); - QPushButton *_skipButton = dialogWidget->findChild("skipButton"); - QPushButton *_closeButton = dialogWidget->findChild("closeButton"); - QLabel *_updateContent = dialogWidget->findChild("updateContent"); + QPushButton *downloadButton = dialogWidget->findChild("downloadButton"); + QPushButton *skipButton = dialogWidget->findChild("skipButton"); + QPushButton *closeButton = dialogWidget->findChild("closeButton"); + QLabel *updateContent = dialogWidget->findChild("updateContent"); - _updateContent->setText(updateRequired); + updateContent->setText(updateRequired); - connect(_downloadButton, SIGNAL(released()), this, SLOT(handleDownload())); - connect(_skipButton, SIGNAL(released()), this, SLOT(handleSkip())); - connect(_closeButton, SIGNAL(released()), this, SLOT(handleClose())); + connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload(QUrl downloadURL))); + connect(skipButton, SIGNAL(released()), this, SLOT(handleSkip())); + connect(closeButton, SIGNAL(released()), this, SLOT(handleClose())); dialogWidget->show(); } -UpdateDialog::~UpdateDialog() { - deleteLater(); -} - -void UpdateDialog::toggleUpdateDialog() { - if (this->dialogWidget->isVisible()) { - this->dialogWidget->hide(); - } else { - this->dialogWidget->show(); - } -} - -void UpdateDialog::handleDownload() { +void UpdateDialog::handleDownload(QUrl downloadURL) { Application* application = Application::getInstance(); - QDesktopServices::openUrl(*application->_downloadURL); + QDesktopServices::openUrl(downloadURL); application->quit(); } void UpdateDialog::handleSkip() { - Application* application = Application::getInstance(); - application->skipVersion(); - this->toggleUpdateDialog(); + this->close(); } void UpdateDialog::handleClose() { - this->toggleUpdateDialog(); + this->close(); } \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 50ab7dacbe..f91424c656 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -21,15 +21,14 @@ class UpdateDialog : public QDialog { Q_OBJECT public: - UpdateDialog(QWidget*, QString releaseNotes); + UpdateDialog(QWidget*, QString releaseNotes, QString latestVersion, QUrl downloadURL); ~UpdateDialog(); - void toggleUpdateDialog(); private: QWidget *dialogWidget; private slots: - void handleDownload(); + void handleDownload(QUrl downloadURL); void handleSkip(); void handleClose(); }; From 93e4dc7d5aa90993568b09193994400a740f2880 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 17 Jan 2014 13:35:49 -0600 Subject: [PATCH 23/58] PR Changes --- interface/src/Application.cpp | 5 +++-- interface/src/Application.h | 6 +++--- interface/src/ui/UpdateDialog.cpp | 13 +++++++------ interface/src/ui/UpdateDialog.h | 3 +-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3cbd590914..8377514d8f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4418,13 +4418,14 @@ void Application::parseVersionXml(QNetworkReply *reply) { } if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) { - updateDialog = new UpdateDialog(_glWidget, releaseNotes); + updateDialog = new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadURL); } } QFile *Application::loadSkipFile() { QString fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); fileName.append(QString("/hifi.skipversion")); + qDebug("###### FILENAME %s \n", fileName.toStdString().c_str()); QFile *file = new QFile(fileName); file->open(QIODevice::ReadWrite); return file; @@ -4435,7 +4436,7 @@ bool Application::shouldSkipVersion(QString latestVersion) { QByteArray skipFileContents = skipFile->readAll(); QString *skipVersion = new QString(skipFileContents); skipFile->close(); - if (*skipVersion == latestVersion || applicationVersion() == "0.1") { + if (*skipVersion == latestVersion /*|| applicationVersion() == "0.1"*/) { return true; } return false; diff --git a/interface/src/Application.h b/interface/src/Application.h index b992748224..ce9f2bbaae 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -250,6 +250,8 @@ private slots: void restoreMirrorView(); void shrinkMirrorView(); void resetSensors(); + + void parseVersionXml(QNetworkReply *reply); private: void resetCamerasOnResizeGL(Camera& camera, int width, int height); @@ -497,13 +499,11 @@ private: QString getLocalVoxelCacheFileName(); void updateLocalOctreeCache(bool firstTime = false); - QFile *loadSkipFile(); + QFile* loadSkipFile(); void checkVersion(); void displayUpdateDialog(); bool shouldSkipVersion(QString latestVersion); void skipVersion(QString latestVersion); - - void parseVersionXml(QNetworkReply *reply); }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 256183682b..5c4ac2c8fc 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -23,7 +23,7 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latest QFile updateDialogUi("resources/ui/updateDialog.ui"); updateDialogUi.open(QFile::ReadOnly); - dialogWidget = updateDialogLoader.load(&updateDialogUi, parent); + _dialogWidget = updateDialogLoader.load(&updateDialogUi, parent); updateDialogUi.close(); const QString updateRequired = QString("You are currently running build %1, the latest build released is %2. \ @@ -33,17 +33,17 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latest setAttribute(Qt::WA_DeleteOnClose); - QPushButton *downloadButton = dialogWidget->findChild("downloadButton"); - QPushButton *skipButton = dialogWidget->findChild("skipButton"); - QPushButton *closeButton = dialogWidget->findChild("closeButton"); - QLabel *updateContent = dialogWidget->findChild("updateContent"); + QPushButton* downloadButton = _dialogWidget->findChild("downloadButton"); + QPushButton* skipButton = _dialogWidget->findChild("skipButton"); + QPushButton* closeButton = _dialogWidget->findChild("closeButton"); + QLabel* updateContent = _dialogWidget->findChild("updateContent"); updateContent->setText(updateRequired); connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload(QUrl downloadURL))); connect(skipButton, SIGNAL(released()), this, SLOT(handleSkip())); connect(closeButton, SIGNAL(released()), this, SLOT(handleClose())); - dialogWidget->show(); + _dialogWidget->show(); } void UpdateDialog::handleDownload(QUrl downloadURL) { @@ -53,6 +53,7 @@ void UpdateDialog::handleDownload(QUrl downloadURL) { } void UpdateDialog::handleSkip() { + this->close(); } diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index f91424c656..dd15cd7efb 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -22,10 +22,9 @@ class UpdateDialog : public QDialog { public: UpdateDialog(QWidget*, QString releaseNotes, QString latestVersion, QUrl downloadURL); - ~UpdateDialog(); private: - QWidget *dialogWidget; + QWidget* _dialogWidget; private slots: void handleDownload(QUrl downloadURL); From 9582fadef4115ac08e0403f548eeec9cfe7aa8ff Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 17 Jan 2014 13:44:03 -0600 Subject: [PATCH 24/58] Last changes --- interface/src/ui/UpdateDialog.cpp | 8 +++++--- interface/src/ui/UpdateDialog.h | 3 --- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 5c4ac2c8fc..2fd03a48f2 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -11,6 +11,9 @@ #include #include #include +#include +#include +#include #include "Application.h" #include "SharedUtil.h" @@ -53,10 +56,9 @@ void UpdateDialog::handleDownload(QUrl downloadURL) { } void UpdateDialog::handleSkip() { - - this->close(); + this->_dialogWidget->close(); } void UpdateDialog::handleClose() { - this->close(); + this->_dialogWidget->close(); } \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index dd15cd7efb..04973620e6 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -10,9 +10,6 @@ #define __hifi__UpdateDialog__ #include -#include -#include -#include #include #include From a4efe55bbf37479daa0aea85fb4c0d9ab10e8214 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 17 Jan 2014 14:02:57 -0600 Subject: [PATCH 25/58] changing dev version --- interface/CMakeLists.txt | 2 +- interface/src/Application.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index c855cef906..d05ea7a7c0 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -15,7 +15,7 @@ set(SIXENSE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense) if (DEFINED ENV{JOB_ID}) set(BUILD_SEQ $ENV{JOB_ID}) else () - set(BUILD_SEQ "0.1") + set(BUILD_SEQ "dev") endif () if (APPLE) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8377514d8f..2284e9fbbf 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4436,7 +4436,7 @@ bool Application::shouldSkipVersion(QString latestVersion) { QByteArray skipFileContents = skipFile->readAll(); QString *skipVersion = new QString(skipFileContents); skipFile->close(); - if (*skipVersion == latestVersion /*|| applicationVersion() == "0.1"*/) { + if (*skipVersion == latestVersion || applicationVersion() == "dev") { return true; } return false; From ff6a9b2052c7c74b8f2ea94e9b6b73f065850e0b Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 17 Jan 2014 14:05:38 -0600 Subject: [PATCH 26/58] PR fix --- interface/src/Application.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2284e9fbbf..4bb40af623 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4372,7 +4372,6 @@ void Application::checkVersion() { QNetworkRequest request(url); connect(downloadXML, SIGNAL(finished(QNetworkReply*)), this, SLOT(parseVersionXml(QNetworkReply*))); downloadXML->get(request); - } void Application::parseVersionXml(QNetworkReply *reply) { @@ -4425,7 +4424,6 @@ void Application::parseVersionXml(QNetworkReply *reply) { QFile *Application::loadSkipFile() { QString fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); fileName.append(QString("/hifi.skipversion")); - qDebug("###### FILENAME %s \n", fileName.toStdString().c_str()); QFile *file = new QFile(fileName); file->open(QIODevice::ReadWrite); return file; From 99275dadd4e99bfc1ab3506b520137ed033b74b2 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 17 Jan 2014 14:39:34 -0600 Subject: [PATCH 27/58] PR Changes --- interface/src/Application.cpp | 30 ++++++++++++++---------------- interface/src/Application.h | 1 - 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4bb40af623..ae82d44e90 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4421,28 +4421,26 @@ void Application::parseVersionXml(QNetworkReply *reply) { } } -QFile *Application::loadSkipFile() { +bool Application::shouldSkipVersion(QString latestVersion) { QString fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); fileName.append(QString("/hifi.skipversion")); - QFile *file = new QFile(fileName); - file->open(QIODevice::ReadWrite); - return file; -} - -bool Application::shouldSkipVersion(QString latestVersion) { - QFile *skipFile = loadSkipFile(); - QByteArray skipFileContents = skipFile->readAll(); - QString *skipVersion = new QString(skipFileContents); - skipFile->close(); - if (*skipVersion == latestVersion || applicationVersion() == "dev") { + QFile skipFile(fileName); + skipFile.open(QIODevice::ReadWrite); + QByteArray skipFileContents = skipFile.readAll(); + QString skipVersion(skipFileContents); + skipFile.close(); + if (skipVersion == latestVersion /*|| applicationVersion() == "dev"*/) { return true; } return false; } void Application::skipVersion(QString latestVersion) { - QFile *skipFile = loadSkipFile(); - skipFile->seek(0); - skipFile->write(latestVersion.toStdString().c_str()); - skipFile->close(); + QString fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); + fileName.append(QString("/hifi.skipversion")); + QFile skipFile(fileName); + skipFile.open(QIODevice::WriteOnly|QIODevice::Truncate); + skipFile.seek(0); + skipFile.write(latestVersion.toStdString().c_str()); + skipFile.close(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index ce9f2bbaae..acb2901452 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -499,7 +499,6 @@ private: QString getLocalVoxelCacheFileName(); void updateLocalOctreeCache(bool firstTime = false); - QFile* loadSkipFile(); void checkVersion(); void displayUpdateDialog(); bool shouldSkipVersion(QString latestVersion); From a13d7b084fcf363450cd78fa7b3f3dbcdd9ac9b7 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Fri, 17 Jan 2014 14:39:48 -0600 Subject: [PATCH 28/58] Removing comment --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ae82d44e90..68f850d01b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4429,7 +4429,7 @@ bool Application::shouldSkipVersion(QString latestVersion) { QByteArray skipFileContents = skipFile.readAll(); QString skipVersion(skipFileContents); skipFile.close(); - if (skipVersion == latestVersion /*|| applicationVersion() == "dev"*/) { + if (skipVersion == latestVersion || applicationVersion() == "dev") { return true; } return false; From cd9f2fd240f2e8864322dc303ff6591c7a8b0a5d Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Sat, 18 Jan 2014 20:17:47 -0600 Subject: [PATCH 29/58] Pull Request fixes --- interface/src/Application.cpp | 51 ++++++++++++------------------- interface/src/Application.h | 5 ++- interface/src/ui/UpdateDialog.cpp | 19 ++++++------ interface/src/ui/UpdateDialog.h | 4 +-- 4 files changed, 36 insertions(+), 43 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 23113dfd71..d8a906e06a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -95,6 +95,7 @@ const float MIRROR_REARVIEW_DISTANCE = 0.65f; const float MIRROR_REARVIEW_BODY_DISTANCE = 2.3f; const QString CHECK_VERSION_URL = "http://highfidelity.io/latestVersion.xml"; +const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/hifi.skipversion"; void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) { QString messageWithNewLine = message + "\n"; @@ -202,8 +203,6 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); - checkVersion(); - qDebug("[VERSION] Build sequence: %s\n", applicationVersion().toStdString().c_str()); _settings = new QSettings(this); @@ -262,7 +261,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : // Set the sixense filtering _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); - + + checkVersion(); } Application::~Application() { @@ -4367,31 +4367,28 @@ void Application::updateLocalOctreeCache(bool firstTime) { void Application::checkVersion() { QUrl url(CHECK_VERSION_URL); - QNetworkAccessManager *downloadXML = new QNetworkAccessManager(this); - QNetworkRequest request(url); - connect(downloadXML, SIGNAL(finished(QNetworkReply*)), this, SLOT(parseVersionXml(QNetworkReply*))); - downloadXML->get(request); + QNetworkRequest latestVersionRequest(url); + latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + _latestVersionReply = getNetworkAccessManager()->get(latestVersionRequest); + + connect(_latestVersionReply, SIGNAL(readyRead()), SLOT(parseVersionXml())); } -void Application::parseVersionXml(QNetworkReply *reply) { - QString *operatingSystem; +void Application::parseVersionXml() { #ifdef Q_OS_WIN32 - operatingSystem = new QString("win"); + QString operatingSystem("win"); #endif #ifdef Q_OS_MAC - operatingSystem = new QString("mac"); + QString operatingSystem("mac"); #endif QString releaseDate; QString releaseNotes; QString latestVersion; - QUrl downloadURL; - QWidget *updateDialog; - - QXmlStreamReader xml(reply); + QXmlStreamReader xml(_latestVersionReply); while (!xml.atEnd() && !xml.hasError()) { QXmlStreamReader::TokenType token = xml.readNext(); @@ -4410,36 +4407,28 @@ void Application::parseVersionXml(QNetworkReply *reply) { } if (xml.name() == operatingSystem) { xml.readNext(); - downloadURL = QUrl(xml.text().toString()); + _downloadUrl = QUrl(xml.text().toString()); } } } if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) { - updateDialog = new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadURL); + new UpdateDialog(_glWidget, releaseNotes, latestVersion, _downloadUrl); } + + delete _latestVersionReply; } bool Application::shouldSkipVersion(QString latestVersion) { - QString fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - fileName.append(QString("/hifi.skipversion")); - QFile skipFile(fileName); + QFile skipFile(SKIP_FILENAME); skipFile.open(QIODevice::ReadWrite); - QByteArray skipFileContents = skipFile.readAll(); - QString skipVersion(skipFileContents); - skipFile.close(); - if (skipVersion == latestVersion || applicationVersion() == "dev") { - return true; - } - return false; + QString skipVersion(skipFile.readAll()); + return (skipVersion == latestVersion /*|| applicationVersion() == "dev"*/); } void Application::skipVersion(QString latestVersion) { - QString fileName = QStandardPaths::writableLocation(QStandardPaths::DataLocation); - fileName.append(QString("/hifi.skipversion")); - QFile skipFile(fileName); + QFile skipFile(SKIP_FILENAME); skipFile.open(QIODevice::WriteOnly|QIODevice::Truncate); skipFile.seek(0); skipFile.write(latestVersion.toStdString().c_str()); - skipFile.close(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index acb2901452..86a162e835 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -203,6 +203,8 @@ public: /// set a voxel which is to be rendered with a highlight void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; } void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; } + + QUrl _downloadUrl; public slots: void domainChanged(const QString& domainHostname); @@ -251,7 +253,7 @@ private slots: void shrinkMirrorView(); void resetSensors(); - void parseVersionXml(QNetworkReply *reply); + void parseVersionXml(); private: void resetCamerasOnResizeGL(Camera& camera, int width, int height); @@ -503,6 +505,7 @@ private: void displayUpdateDialog(); bool shouldSkipVersion(QString latestVersion); void skipVersion(QString latestVersion); + QNetworkReply* _latestVersionReply; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 2fd03a48f2..5ee6cd6812 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -19,17 +19,18 @@ #include "SharedUtil.h" #include "UpdateDialog.h" -UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latestVersion, QUrl downloadURL) : QDialog(parent, Qt::Dialog) { +UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latestVersion, QUrl downloadURL) : + QDialog(parent, Qt::Dialog) { + Application* application = Application::getInstance(); QUiLoader updateDialogLoader; QFile updateDialogUi("resources/ui/updateDialog.ui"); updateDialogUi.open(QFile::ReadOnly); - _dialogWidget = updateDialogLoader.load(&updateDialogUi, parent); - updateDialogUi.close(); + _dialogWidget = updateDialogLoader.load(&updateDialogUi, this); - const QString updateRequired = QString("You are currently running build %1, the latest build released is %2. \ + QString updateRequired = QString("You are currently running build %1, the latest build released is %2. \ Please download and install the most recent release to access the latest features and bug fixes.") .arg(application->applicationVersion(), latestVersion); @@ -43,22 +44,22 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latest updateContent->setText(updateRequired); - connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload(QUrl downloadURL))); + connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload())); connect(skipButton, SIGNAL(released()), this, SLOT(handleSkip())); connect(closeButton, SIGNAL(released()), this, SLOT(handleClose())); _dialogWidget->show(); } -void UpdateDialog::handleDownload(QUrl downloadURL) { +void UpdateDialog::handleDownload() { Application* application = Application::getInstance(); - QDesktopServices::openUrl(downloadURL); + QDesktopServices::openUrl(application->_downloadUrl); application->quit(); } void UpdateDialog::handleSkip() { - this->_dialogWidget->close(); + this->close(); } void UpdateDialog::handleClose() { - this->_dialogWidget->close(); + this->close(); } \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 04973620e6..690d0a9f0d 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -18,13 +18,13 @@ class UpdateDialog : public QDialog { Q_OBJECT public: - UpdateDialog(QWidget*, QString releaseNotes, QString latestVersion, QUrl downloadURL); + UpdateDialog(QWidget* parent, QString releaseNotes, QString latestVersion, QUrl downloadURL); private: QWidget* _dialogWidget; private slots: - void handleDownload(QUrl downloadURL); + void handleDownload(); void handleSkip(); void handleClose(); }; From 7748002db7ad433431761e4771bb4e90b26545bb Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Sat, 18 Jan 2014 20:27:56 -0600 Subject: [PATCH 30/58] More PR related changes --- interface/src/Application.cpp | 4 ++++ interface/src/Application.h | 2 +- interface/src/ui/UpdateDialog.cpp | 6 +++++- interface/src/ui/UpdateDialog.h | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d8a906e06a..18202ee1b6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4384,6 +4384,10 @@ void Application::parseVersionXml() { QString operatingSystem("mac"); #endif + #ifdef Q_OS_LINUX + QString operatingSystem("ubuntu"); + #endif + QString releaseDate; QString releaseNotes; QString latestVersion; diff --git a/interface/src/Application.h b/interface/src/Application.h index 86a162e835..d66042149d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -205,6 +205,7 @@ public: void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; } QUrl _downloadUrl; + void skipVersion(QString latestVersion); public slots: void domainChanged(const QString& domainHostname); @@ -504,7 +505,6 @@ private: void checkVersion(); void displayUpdateDialog(); bool shouldSkipVersion(QString latestVersion); - void skipVersion(QString latestVersion); QNetworkReply* _latestVersionReply; }; diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 5ee6cd6812..adc2139634 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -20,10 +20,12 @@ #include "UpdateDialog.h" UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latestVersion, QUrl downloadURL) : - QDialog(parent, Qt::Dialog) { + QWidget(parent, Qt::Widget) { Application* application = Application::getInstance(); + QString _latestVersion(latestVersion); + QUiLoader updateDialogLoader; QFile updateDialogUi("resources/ui/updateDialog.ui"); @@ -57,6 +59,8 @@ void UpdateDialog::handleDownload() { } void UpdateDialog::handleSkip() { + Application* application = Application::getInstance(); + application->skipVersion(_latestVersion); this->close(); } diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 690d0a9f0d..6faca45974 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -14,7 +14,7 @@ #include -class UpdateDialog : public QDialog { +class UpdateDialog : public QWidget { Q_OBJECT public: @@ -22,6 +22,7 @@ public: private: QWidget* _dialogWidget; + QString _latestVersion; private slots: void handleDownload(); From 3d5d02d5b0c4e4b0f0f02101f4230ec382b8a412 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Sat, 18 Jan 2014 20:33:29 -0600 Subject: [PATCH 31/58] Some more changes --- interface/src/ui/UpdateDialog.cpp | 8 ++++---- interface/src/ui/UpdateDialog.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index adc2139634..8a06d69b3f 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -24,7 +24,7 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latest Application* application = Application::getInstance(); - QString _latestVersion(latestVersion); + _latestVersion = new QString(latestVersion); QUiLoader updateDialogLoader; @@ -33,8 +33,8 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latest _dialogWidget = updateDialogLoader.load(&updateDialogUi, this); QString updateRequired = QString("You are currently running build %1, the latest build released is %2. \ - Please download and install the most recent release to access the latest features and bug fixes.") - .arg(application->applicationVersion(), latestVersion); + Please download and install the most recent release to access the latest features and bug fixes.") + .arg(application->applicationVersion(), latestVersion); setAttribute(Qt::WA_DeleteOnClose); @@ -60,7 +60,7 @@ void UpdateDialog::handleDownload() { void UpdateDialog::handleSkip() { Application* application = Application::getInstance(); - application->skipVersion(_latestVersion); + application->skipVersion(*_latestVersion); this->close(); } diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index 6faca45974..e7ea79a9d9 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -22,7 +22,7 @@ public: private: QWidget* _dialogWidget; - QString _latestVersion; + QString* _latestVersion; private slots: void handleDownload(); From 825c64e1d0141353b0b7e0898e4cec76511ae445 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 20 Jan 2014 10:57:10 -0600 Subject: [PATCH 32/58] Anything obviously wrong? --- interface/src/Application.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 18202ee1b6..96687a55c3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4366,10 +4366,10 @@ void Application::updateLocalOctreeCache(bool firstTime) { } void Application::checkVersion() { - QUrl url(CHECK_VERSION_URL); - QNetworkRequest latestVersionRequest(url); + qDebug("############# in check version"); + QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL))); latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); - _latestVersionReply = getNetworkAccessManager()->get(latestVersionRequest); + _latestVersionReply = Application::getInstance()->getNetworkAccessManager()->get(latestVersionRequest); connect(_latestVersionReply, SIGNAL(readyRead()), SLOT(parseVersionXml())); } @@ -4432,6 +4432,7 @@ bool Application::shouldSkipVersion(QString latestVersion) { void Application::skipVersion(QString latestVersion) { QFile skipFile(SKIP_FILENAME); + qDebug("##### Storing %s in file %s", latestVersion.toStdString().c_str(), SKIP_FILENAME.toStdString().c_str()); skipFile.open(QIODevice::WriteOnly|QIODevice::Truncate); skipFile.seek(0); skipFile.write(latestVersion.toStdString().c_str()); From f6bc7a2c4bf9254a50ac0c591a474b454c3a2408 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 20 Jan 2014 11:51:17 -0600 Subject: [PATCH 33/58] I think last fixes --- interface/src/ui/UpdateDialog.cpp | 18 ++++++++---------- interface/src/ui/UpdateDialog.h | 1 - 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 8a06d69b3f..ae3a52932f 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -22,34 +22,32 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latestVersion, QUrl downloadURL) : QWidget(parent, Qt::Widget) { - Application* application = Application::getInstance(); - _latestVersion = new QString(latestVersion); QUiLoader updateDialogLoader; - + QWidget* updateDialog; QFile updateDialogUi("resources/ui/updateDialog.ui"); updateDialogUi.open(QFile::ReadOnly); - _dialogWidget = updateDialogLoader.load(&updateDialogUi, this); + updateDialog = updateDialogLoader.load(&updateDialogUi, this); QString updateRequired = QString("You are currently running build %1, the latest build released is %2. \ Please download and install the most recent release to access the latest features and bug fixes.") - .arg(application->applicationVersion(), latestVersion); + .arg(Application::getInstance()->applicationVersion(), latestVersion); setAttribute(Qt::WA_DeleteOnClose); - QPushButton* downloadButton = _dialogWidget->findChild("downloadButton"); - QPushButton* skipButton = _dialogWidget->findChild("skipButton"); - QPushButton* closeButton = _dialogWidget->findChild("closeButton"); - QLabel* updateContent = _dialogWidget->findChild("updateContent"); + QPushButton* downloadButton = updateDialog->findChild("downloadButton"); + QPushButton* skipButton = updateDialog->findChild("skipButton"); + QPushButton* closeButton = updateDialog->findChild("closeButton"); + QLabel* updateContent = updateDialog->findChild("updateContent"); updateContent->setText(updateRequired); connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload())); connect(skipButton, SIGNAL(released()), this, SLOT(handleSkip())); connect(closeButton, SIGNAL(released()), this, SLOT(handleClose())); - _dialogWidget->show(); + updateDialog->show(); } void UpdateDialog::handleDownload() { diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index e7ea79a9d9..aa0b800528 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -21,7 +21,6 @@ public: UpdateDialog(QWidget* parent, QString releaseNotes, QString latestVersion, QUrl downloadURL); private: - QWidget* _dialogWidget; QString* _latestVersion; private slots: From 04970be8a21c5b8193b942cc873a3abf03616723 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 20 Jan 2014 11:52:26 -0600 Subject: [PATCH 34/58] Removing comments --- interface/src/Application.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 96687a55c3..0d1435d5df 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4427,12 +4427,11 @@ bool Application::shouldSkipVersion(QString latestVersion) { QFile skipFile(SKIP_FILENAME); skipFile.open(QIODevice::ReadWrite); QString skipVersion(skipFile.readAll()); - return (skipVersion == latestVersion /*|| applicationVersion() == "dev"*/); + return (skipVersion == latestVersion || applicationVersion() == "dev"); } void Application::skipVersion(QString latestVersion) { QFile skipFile(SKIP_FILENAME); - qDebug("##### Storing %s in file %s", latestVersion.toStdString().c_str(), SKIP_FILENAME.toStdString().c_str()); skipFile.open(QIODevice::WriteOnly|QIODevice::Truncate); skipFile.seek(0); skipFile.write(latestVersion.toStdString().c_str()); From 3036142a389eaab3443a8f26ac3d6f71623f1fa9 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Mon, 20 Jan 2014 11:55:10 -0600 Subject: [PATCH 35/58] Removing comments --- interface/src/Application.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0d1435d5df..6c9dcae410 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4366,7 +4366,6 @@ void Application::updateLocalOctreeCache(bool firstTime) { } void Application::checkVersion() { - qDebug("############# in check version"); QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL))); latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); _latestVersionReply = Application::getInstance()->getNetworkAccessManager()->get(latestVersionRequest); @@ -4432,7 +4431,7 @@ bool Application::shouldSkipVersion(QString latestVersion) { void Application::skipVersion(QString latestVersion) { QFile skipFile(SKIP_FILENAME); - skipFile.open(QIODevice::WriteOnly|QIODevice::Truncate); + skipFile.open(QIODevice::WriteOnly | QIODevice::Truncate); skipFile.seek(0); skipFile.write(latestVersion.toStdString().c_str()); } From d405d31edf0d1cf4b5ac89d4b542fdf04f045807 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Jan 2014 14:42:50 -0800 Subject: [PATCH 36/58] add data-server to hifi repo, abstract DataServerClient --- CMakeLists.txt | 1 + data-server/CMakeLists.txt | 23 + data-server/external/hiredis/async.c | 648 ++++++++++++ data-server/external/hiredis/async.h | 125 +++ data-server/external/hiredis/dict.c | 338 ++++++ data-server/external/hiredis/dict.h | 126 +++ data-server/external/hiredis/fmacros.h | 20 + data-server/external/hiredis/hiredis.c | 1322 ++++++++++++++++++++++++ data-server/external/hiredis/hiredis.h | 213 ++++ data-server/external/hiredis/net.c | 339 ++++++ data-server/external/hiredis/net.h | 48 + data-server/external/hiredis/sds.c | 606 +++++++++++ data-server/external/hiredis/sds.h | 88 ++ data-server/src/DataServer.cpp | 194 ++++ data-server/src/DataServer.h | 29 + data-server/src/main.cpp | 24 + interface/src/Application.cpp | 16 +- interface/src/Application.h | 2 +- interface/src/DataServerClient.cpp | 290 +++--- interface/src/DataServerClient.h | 31 +- interface/src/Menu.cpp | 2 +- interface/src/avatar/Profile.cpp | 29 +- interface/src/avatar/Profile.h | 9 +- 23 files changed, 4355 insertions(+), 168 deletions(-) create mode 100755 data-server/CMakeLists.txt create mode 100755 data-server/external/hiredis/async.c create mode 100755 data-server/external/hiredis/async.h create mode 100755 data-server/external/hiredis/dict.c create mode 100755 data-server/external/hiredis/dict.h create mode 100755 data-server/external/hiredis/fmacros.h create mode 100755 data-server/external/hiredis/hiredis.c create mode 100755 data-server/external/hiredis/hiredis.h create mode 100755 data-server/external/hiredis/net.c create mode 100755 data-server/external/hiredis/net.h create mode 100755 data-server/external/hiredis/sds.c create mode 100755 data-server/external/hiredis/sds.h create mode 100644 data-server/src/DataServer.cpp create mode 100644 data-server/src/DataServer.h create mode 100755 data-server/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e01986b355..e0a03036ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ endif (NOT WIN32) # targets on all platforms add_subdirectory(assignment-client) +add_subdirectory(data-server) add_subdirectory(domain-server) add_subdirectory(interface) add_subdirectory(pairing-server) diff --git a/data-server/CMakeLists.txt b/data-server/CMakeLists.txt new file mode 100755 index 0000000000..65e3e4a489 --- /dev/null +++ b/data-server/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 2.8) + +set(ROOT_DIR ..) +set(MACRO_DIR ${ROOT_DIR}/cmake/macros) + +set(TARGET_NAME data-server) + +find_package(Qt5Network REQUIRED) + +include(${MACRO_DIR}/SetupHifiProject.cmake) +setup_hifi_project(${TARGET_NAME} TRUE) + +qt5_use_modules(${TARGET_NAME} Network) + +# link the shared hifi library +include(${MACRO_DIR}/LinkHifiLibrary.cmake) +link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) + +# add hiredis as a library +FILE(GLOB HIREDIS_SRCS external/hiredis/*.c) +add_library(hiredis ${HIREDIS_SRCS}) +include_directories(external/hiredis/) +target_link_libraries(${TARGET_NAME} hiredis) \ No newline at end of file diff --git a/data-server/external/hiredis/async.c b/data-server/external/hiredis/async.c new file mode 100755 index 0000000000..65327120fe --- /dev/null +++ b/data-server/external/hiredis/async.c @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include +#include +#include +#include +#include +#include +#include "async.h" +#include "net.h" +#include "dict.c" +#include "sds.h" + +#define _EL_ADD_READ(ctx) do { \ + if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \ + } while(0) +#define _EL_DEL_READ(ctx) do { \ + if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \ + } while(0) +#define _EL_ADD_WRITE(ctx) do { \ + if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \ + } while(0) +#define _EL_DEL_WRITE(ctx) do { \ + if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \ + } while(0) +#define _EL_CLEANUP(ctx) do { \ + if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \ + } while(0); + +/* Forward declaration of function in hiredis.c */ +void __redisAppendCommand(redisContext *c, char *cmd, size_t len); + +/* Functions managing dictionary of callbacks for pub/sub. */ +static unsigned int callbackHash(const void *key) { + return dictGenHashFunction((const unsigned char *)key, + sdslen((const sds)key)); +} + +static void *callbackValDup(void *privdata, const void *src) { + ((void) privdata); + redisCallback *dup = malloc(sizeof(*dup)); + memcpy(dup,src,sizeof(*dup)); + return dup; +} + +static int callbackKeyCompare(void *privdata, const void *key1, const void *key2) { + int l1, l2; + ((void) privdata); + + l1 = sdslen((const sds)key1); + l2 = sdslen((const sds)key2); + if (l1 != l2) return 0; + return memcmp(key1,key2,l1) == 0; +} + +static void callbackKeyDestructor(void *privdata, void *key) { + ((void) privdata); + sdsfree((sds)key); +} + +static void callbackValDestructor(void *privdata, void *val) { + ((void) privdata); + free(val); +} + +static dictType callbackDict = { + callbackHash, + NULL, + callbackValDup, + callbackKeyCompare, + callbackKeyDestructor, + callbackValDestructor +}; + +static redisAsyncContext *redisAsyncInitialize(redisContext *c) { + redisAsyncContext *ac; + + ac = realloc(c,sizeof(redisAsyncContext)); + if (ac == NULL) + return NULL; + + c = &(ac->c); + + /* The regular connect functions will always set the flag REDIS_CONNECTED. + * For the async API, we want to wait until the first write event is + * received up before setting this flag, so reset it here. */ + c->flags &= ~REDIS_CONNECTED; + + ac->err = 0; + ac->errstr = NULL; + ac->data = NULL; + + ac->ev.data = NULL; + ac->ev.addRead = NULL; + ac->ev.delRead = NULL; + ac->ev.addWrite = NULL; + ac->ev.delWrite = NULL; + ac->ev.cleanup = NULL; + + ac->onConnect = NULL; + ac->onDisconnect = NULL; + + ac->replies.head = NULL; + ac->replies.tail = NULL; + ac->sub.invalid.head = NULL; + ac->sub.invalid.tail = NULL; + ac->sub.channels = dictCreate(&callbackDict,NULL); + ac->sub.patterns = dictCreate(&callbackDict,NULL); + return ac; +} + +/* We want the error field to be accessible directly instead of requiring + * an indirection to the redisContext struct. */ +static void __redisAsyncCopyError(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + ac->err = c->err; + ac->errstr = c->errstr; +} + +redisAsyncContext *redisAsyncConnect(const char *ip, int port) { + redisContext *c; + redisAsyncContext *ac; + + c = redisConnectNonBlock(ip,port); + if (c == NULL) + return NULL; + + ac = redisAsyncInitialize(c); + if (ac == NULL) { + redisFree(c); + return NULL; + } + + __redisAsyncCopyError(ac); + return ac; +} + +redisAsyncContext *redisAsyncConnectUnix(const char *path) { + redisContext *c; + redisAsyncContext *ac; + + c = redisConnectUnixNonBlock(path); + if (c == NULL) + return NULL; + + ac = redisAsyncInitialize(c); + __redisAsyncCopyError(ac); + return ac; +} + +int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) { + if (ac->onConnect == NULL) { + ac->onConnect = fn; + + /* The common way to detect an established connection is to wait for + * the first write event to be fired. This assumes the related event + * library functions are already set. */ + _EL_ADD_WRITE(ac); + return REDIS_OK; + } + return REDIS_ERR; +} + +int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) { + if (ac->onDisconnect == NULL) { + ac->onDisconnect = fn; + return REDIS_OK; + } + return REDIS_ERR; +} + +/* Helper functions to push/shift callbacks */ +static int __redisPushCallback(redisCallbackList *list, redisCallback *source) { + redisCallback *cb; + + /* Copy callback from stack to heap */ + cb = malloc(sizeof(*cb)); + if (cb == NULL) + return REDIS_ERR_OOM; + + if (source != NULL) { + memcpy(cb,source,sizeof(*cb)); + cb->next = NULL; + } + + /* Store callback in list */ + if (list->head == NULL) + list->head = cb; + if (list->tail != NULL) + list->tail->next = cb; + list->tail = cb; + return REDIS_OK; +} + +static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) { + redisCallback *cb = list->head; + if (cb != NULL) { + list->head = cb->next; + if (cb == list->tail) + list->tail = NULL; + + /* Copy callback from heap to stack */ + if (target != NULL) + memcpy(target,cb,sizeof(*cb)); + free(cb); + return REDIS_OK; + } + return REDIS_ERR; +} + +static void __redisRunCallback(redisAsyncContext *ac, redisCallback *cb, redisReply *reply) { + redisContext *c = &(ac->c); + if (cb->fn != NULL) { + c->flags |= REDIS_IN_CALLBACK; + cb->fn(ac,reply,cb->privdata); + c->flags &= ~REDIS_IN_CALLBACK; + } +} + +/* Helper function to free the context. */ +static void __redisAsyncFree(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + redisCallback cb; + dictIterator *it; + dictEntry *de; + + /* Execute pending callbacks with NULL reply. */ + while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK) + __redisRunCallback(ac,&cb,NULL); + + /* Execute callbacks for invalid commands */ + while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK) + __redisRunCallback(ac,&cb,NULL); + + /* Run subscription callbacks callbacks with NULL reply */ + it = dictGetIterator(ac->sub.channels); + while ((de = dictNext(it)) != NULL) + __redisRunCallback(ac,dictGetEntryVal(de),NULL); + dictReleaseIterator(it); + dictRelease(ac->sub.channels); + + it = dictGetIterator(ac->sub.patterns); + while ((de = dictNext(it)) != NULL) + __redisRunCallback(ac,dictGetEntryVal(de),NULL); + dictReleaseIterator(it); + dictRelease(ac->sub.patterns); + + /* Signal event lib to clean up */ + _EL_CLEANUP(ac); + + /* Execute disconnect callback. When redisAsyncFree() initiated destroying + * this context, the status will always be REDIS_OK. */ + if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) { + if (c->flags & REDIS_FREEING) { + ac->onDisconnect(ac,REDIS_OK); + } else { + ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR); + } + } + + /* Cleanup self */ + redisFree(c); +} + +/* Free the async context. When this function is called from a callback, + * control needs to be returned to redisProcessCallbacks() before actual + * free'ing. To do so, a flag is set on the context which is picked up by + * redisProcessCallbacks(). Otherwise, the context is immediately free'd. */ +void redisAsyncFree(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + c->flags |= REDIS_FREEING; + if (!(c->flags & REDIS_IN_CALLBACK)) + __redisAsyncFree(ac); +} + +/* Helper function to make the disconnect happen and clean up. */ +static void __redisAsyncDisconnect(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + + /* Make sure error is accessible if there is any */ + __redisAsyncCopyError(ac); + + if (ac->err == 0) { + /* For clean disconnects, there should be no pending callbacks. */ + assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR); + } else { + /* Disconnection is caused by an error, make sure that pending + * callbacks cannot call new commands. */ + c->flags |= REDIS_DISCONNECTING; + } + + /* For non-clean disconnects, __redisAsyncFree() will execute pending + * callbacks with a NULL-reply. */ + __redisAsyncFree(ac); +} + +/* Tries to do a clean disconnect from Redis, meaning it stops new commands + * from being issued, but tries to flush the output buffer and execute + * callbacks for all remaining replies. When this function is called from a + * callback, there might be more replies and we can safely defer disconnecting + * to redisProcessCallbacks(). Otherwise, we can only disconnect immediately + * when there are no pending callbacks. */ +void redisAsyncDisconnect(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + c->flags |= REDIS_DISCONNECTING; + if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL) + __redisAsyncDisconnect(ac); +} + +static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply, redisCallback *dstcb) { + redisContext *c = &(ac->c); + dict *callbacks; + dictEntry *de; + int pvariant; + char *stype; + sds sname; + + /* Custom reply functions are not supported for pub/sub. This will fail + * very hard when they are used... */ + if (reply->type == REDIS_REPLY_ARRAY) { + assert(reply->elements >= 2); + assert(reply->element[0]->type == REDIS_REPLY_STRING); + stype = reply->element[0]->str; + pvariant = (tolower(stype[0]) == 'p') ? 1 : 0; + + if (pvariant) + callbacks = ac->sub.patterns; + else + callbacks = ac->sub.channels; + + /* Locate the right callback */ + assert(reply->element[1]->type == REDIS_REPLY_STRING); + sname = sdsnewlen(reply->element[1]->str,reply->element[1]->len); + de = dictFind(callbacks,sname); + if (de != NULL) { + memcpy(dstcb,dictGetEntryVal(de),sizeof(*dstcb)); + + /* If this is an unsubscribe message, remove it. */ + if (strcasecmp(stype+pvariant,"unsubscribe") == 0) { + dictDelete(callbacks,sname); + + /* If this was the last unsubscribe message, revert to + * non-subscribe mode. */ + assert(reply->element[2]->type == REDIS_REPLY_INTEGER); + if (reply->element[2]->integer == 0) + c->flags &= ~REDIS_SUBSCRIBED; + } + } + sdsfree(sname); + } else { + /* Shift callback for invalid commands. */ + __redisShiftCallback(&ac->sub.invalid,dstcb); + } + return REDIS_OK; +} + +void redisProcessCallbacks(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + redisCallback cb; + void *reply = NULL; + int status; + + while((status = redisGetReply(c,&reply)) == REDIS_OK) { + if (reply == NULL) { + /* When the connection is being disconnected and there are + * no more replies, this is the cue to really disconnect. */ + if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) { + __redisAsyncDisconnect(ac); + return; + } + + /* If monitor mode, repush callback */ + if(c->flags & REDIS_MONITORING) { + __redisPushCallback(&ac->replies,&cb); + } + + /* When the connection is not being disconnected, simply stop + * trying to get replies and wait for the next loop tick. */ + break; + } + + /* Even if the context is subscribed, pending regular callbacks will + * get a reply before pub/sub messages arrive. */ + if (__redisShiftCallback(&ac->replies,&cb) != REDIS_OK) { + /* + * A spontaneous reply in a not-subscribed context can be the error + * reply that is sent when a new connection exceeds the maximum + * number of allowed connections on the server side. + * + * This is seen as an error instead of a regular reply because the + * server closes the connection after sending it. + * + * To prevent the error from being overwritten by an EOF error the + * connection is closed here. See issue #43. + * + * Another possibility is that the server is loading its dataset. + * In this case we also want to close the connection, and have the + * user wait until the server is ready to take our request. + */ + if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) { + c->err = REDIS_ERR_OTHER; + snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str); + __redisAsyncDisconnect(ac); + return; + } + /* No more regular callbacks and no errors, the context *must* be subscribed or monitoring. */ + assert((c->flags & REDIS_SUBSCRIBED || c->flags & REDIS_MONITORING)); + if(c->flags & REDIS_SUBSCRIBED) + __redisGetSubscribeCallback(ac,reply,&cb); + } + + if (cb.fn != NULL) { + __redisRunCallback(ac,&cb,reply); + c->reader->fn->freeObject(reply); + + /* Proceed with free'ing when redisAsyncFree() was called. */ + if (c->flags & REDIS_FREEING) { + __redisAsyncFree(ac); + return; + } + } else { + /* No callback for this reply. This can either be a NULL callback, + * or there were no callbacks to begin with. Either way, don't + * abort with an error, but simply ignore it because the client + * doesn't know what the server will spit out over the wire. */ + c->reader->fn->freeObject(reply); + } + } + + /* Disconnect when there was an error reading the reply */ + if (status != REDIS_OK) + __redisAsyncDisconnect(ac); +} + +/* Internal helper function to detect socket status the first time a read or + * write event fires. When connecting was not succesful, the connect callback + * is called with a REDIS_ERR status and the context is free'd. */ +static int __redisAsyncHandleConnect(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + + if (redisCheckSocketError(c,c->fd) == REDIS_ERR) { + /* Try again later when connect(2) is still in progress. */ + if (errno == EINPROGRESS) + return REDIS_OK; + + if (ac->onConnect) ac->onConnect(ac,REDIS_ERR); + __redisAsyncDisconnect(ac); + return REDIS_ERR; + } + + /* Mark context as connected. */ + c->flags |= REDIS_CONNECTED; + if (ac->onConnect) ac->onConnect(ac,REDIS_OK); + return REDIS_OK; +} + +/* This function should be called when the socket is readable. + * It processes all replies that can be read and executes their callbacks. + */ +void redisAsyncHandleRead(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + + if (!(c->flags & REDIS_CONNECTED)) { + /* Abort connect was not successful. */ + if (__redisAsyncHandleConnect(ac) != REDIS_OK) + return; + /* Try again later when the context is still not connected. */ + if (!(c->flags & REDIS_CONNECTED)) + return; + } + + if (redisBufferRead(c) == REDIS_ERR) { + __redisAsyncDisconnect(ac); + } else { + /* Always re-schedule reads */ + _EL_ADD_READ(ac); + redisProcessCallbacks(ac); + } +} + +void redisAsyncHandleWrite(redisAsyncContext *ac) { + redisContext *c = &(ac->c); + int done = 0; + + if (!(c->flags & REDIS_CONNECTED)) { + /* Abort connect was not successful. */ + if (__redisAsyncHandleConnect(ac) != REDIS_OK) + return; + /* Try again later when the context is still not connected. */ + if (!(c->flags & REDIS_CONNECTED)) + return; + } + + if (redisBufferWrite(c,&done) == REDIS_ERR) { + __redisAsyncDisconnect(ac); + } else { + /* Continue writing when not done, stop writing otherwise */ + if (!done) + _EL_ADD_WRITE(ac); + else + _EL_DEL_WRITE(ac); + + /* Always schedule reads after writes */ + _EL_ADD_READ(ac); + } +} + +/* Sets a pointer to the first argument and its length starting at p. Returns + * the number of bytes to skip to get to the following argument. */ +static char *nextArgument(char *start, char **str, size_t *len) { + char *p = start; + if (p[0] != '$') { + p = strchr(p,'$'); + if (p == NULL) return NULL; + } + + *len = (int)strtol(p+1,NULL,10); + p = strchr(p,'\r'); + assert(p); + *str = p+2; + return p+2+(*len)+2; +} + +/* Helper function for the redisAsyncCommand* family of functions. Writes a + * formatted command to the output buffer and registers the provided callback + * function with the context. */ +static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, char *cmd, size_t len) { + redisContext *c = &(ac->c); + redisCallback cb; + int pvariant, hasnext; + char *cstr, *astr; + size_t clen, alen; + char *p; + sds sname; + + /* Don't accept new commands when the connection is about to be closed. */ + if (c->flags & (REDIS_DISCONNECTING | REDIS_FREEING)) return REDIS_ERR; + + /* Setup callback */ + cb.fn = fn; + cb.privdata = privdata; + + /* Find out which command will be appended. */ + p = nextArgument(cmd,&cstr,&clen); + assert(p != NULL); + hasnext = (p[0] == '$'); + pvariant = (tolower(cstr[0]) == 'p') ? 1 : 0; + cstr += pvariant; + clen -= pvariant; + + if (hasnext && strncasecmp(cstr,"subscribe\r\n",11) == 0) { + c->flags |= REDIS_SUBSCRIBED; + + /* Add every channel/pattern to the list of subscription callbacks. */ + while ((p = nextArgument(p,&astr,&alen)) != NULL) { + sname = sdsnewlen(astr,alen); + if (pvariant) + dictReplace(ac->sub.patterns,sname,&cb); + else + dictReplace(ac->sub.channels,sname,&cb); + } + } else if (strncasecmp(cstr,"unsubscribe\r\n",13) == 0) { + /* It is only useful to call (P)UNSUBSCRIBE when the context is + * subscribed to one or more channels or patterns. */ + if (!(c->flags & REDIS_SUBSCRIBED)) return REDIS_ERR; + + /* (P)UNSUBSCRIBE does not have its own response: every channel or + * pattern that is unsubscribed will receive a message. This means we + * should not append a callback function for this command. */ + } else if(strncasecmp(cstr,"monitor\r\n",9) == 0) { + /* Set monitor flag and push callback */ + c->flags |= REDIS_MONITORING; + __redisPushCallback(&ac->replies,&cb); + } else { + if (c->flags & REDIS_SUBSCRIBED) + /* This will likely result in an error reply, but it needs to be + * received and passed to the callback. */ + __redisPushCallback(&ac->sub.invalid,&cb); + else + __redisPushCallback(&ac->replies,&cb); + } + + __redisAppendCommand(c,cmd,len); + + /* Always schedule a write when the write buffer is non-empty */ + _EL_ADD_WRITE(ac); + + return REDIS_OK; +} + +int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) { + char *cmd; + int len; + int status; + len = redisvFormatCommand(&cmd,format,ap); + status = __redisAsyncCommand(ac,fn,privdata,cmd,len); + free(cmd); + return status; +} + +int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) { + va_list ap; + int status; + va_start(ap,format); + status = redisvAsyncCommand(ac,fn,privdata,format,ap); + va_end(ap); + return status; +} + +int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) { + char *cmd; + int len; + int status; + len = redisFormatCommandArgv(&cmd,argc,argv,argvlen); + status = __redisAsyncCommand(ac,fn,privdata,cmd,len); + free(cmd); + return status; +} diff --git a/data-server/external/hiredis/async.h b/data-server/external/hiredis/async.h new file mode 100755 index 0000000000..268274e8e7 --- /dev/null +++ b/data-server/external/hiredis/async.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_ASYNC_H +#define __HIREDIS_ASYNC_H +#include "hiredis.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct redisAsyncContext; /* need forward declaration of redisAsyncContext */ +struct dict; /* dictionary header is included in async.c */ + +/* Reply callback prototype and container */ +typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*); +typedef struct redisCallback { + struct redisCallback *next; /* simple singly linked list */ + redisCallbackFn *fn; + void *privdata; +} redisCallback; + +/* List of callbacks for either regular replies or pub/sub */ +typedef struct redisCallbackList { + redisCallback *head, *tail; +} redisCallbackList; + +/* Connection callback prototypes */ +typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status); +typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status); + +/* Context for an async connection to Redis */ +typedef struct redisAsyncContext { + /* Hold the regular context, so it can be realloc'ed. */ + redisContext c; + + /* Setup error flags so they can be used directly. */ + int err; + char *errstr; + + /* Not used by hiredis */ + void *data; + + /* Event library data and hooks */ + struct { + void *data; + + /* Hooks that are called when the library expects to start + * reading/writing. These functions should be idempotent. */ + void (*addRead)(void *privdata); + void (*delRead)(void *privdata); + void (*addWrite)(void *privdata); + void (*delWrite)(void *privdata); + void (*cleanup)(void *privdata); + } ev; + + /* Called when either the connection is terminated due to an error or per + * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */ + redisDisconnectCallback *onDisconnect; + + /* Called when the first write event was received. */ + redisConnectCallback *onConnect; + + /* Regular command callbacks */ + redisCallbackList replies; + + /* Subscription callbacks */ + struct { + redisCallbackList invalid; + struct dict *channels; + struct dict *patterns; + } sub; +} redisAsyncContext; + +/* Functions that proxy to hiredis */ +redisAsyncContext *redisAsyncConnect(const char *ip, int port); +redisAsyncContext *redisAsyncConnectUnix(const char *path); +int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); +int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); +void redisAsyncDisconnect(redisAsyncContext *ac); +void redisAsyncFree(redisAsyncContext *ac); + +/* Handle read/write events */ +void redisAsyncHandleRead(redisAsyncContext *ac); +void redisAsyncHandleWrite(redisAsyncContext *ac); + +/* Command functions for an async context. Write the command to the + * output buffer and register the provided callback. */ +int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap); +int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...); +int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/data-server/external/hiredis/dict.c b/data-server/external/hiredis/dict.c new file mode 100755 index 0000000000..79b1041cad --- /dev/null +++ b/data-server/external/hiredis/dict.c @@ -0,0 +1,338 @@ +/* Hash table implementation. + * + * This file implements in memory hash tables with insert/del/replace/find/ + * get-random-element operations. Hash tables will auto resize if needed + * tables of power of two in size are used, collisions are handled by + * chaining. See the source code for more information... :) + * + * Copyright (c) 2006-2010, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include +#include +#include +#include "dict.h" + +/* -------------------------- private prototypes ---------------------------- */ + +static int _dictExpandIfNeeded(dict *ht); +static unsigned long _dictNextPower(unsigned long size); +static int _dictKeyIndex(dict *ht, const void *key); +static int _dictInit(dict *ht, dictType *type, void *privDataPtr); + +/* -------------------------- hash functions -------------------------------- */ + +/* Generic hash function (a popular one from Bernstein). + * I tested a few and this was the best. */ +static unsigned int dictGenHashFunction(const unsigned char *buf, int len) { + unsigned int hash = 5381; + + while (len--) + hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */ + return hash; +} + +/* ----------------------------- API implementation ------------------------- */ + +/* Reset an hashtable already initialized with ht_init(). + * NOTE: This function should only called by ht_destroy(). */ +static void _dictReset(dict *ht) { + ht->table = NULL; + ht->size = 0; + ht->sizemask = 0; + ht->used = 0; +} + +/* Create a new hash table */ +static dict *dictCreate(dictType *type, void *privDataPtr) { + dict *ht = malloc(sizeof(*ht)); + _dictInit(ht,type,privDataPtr); + return ht; +} + +/* Initialize the hash table */ +static int _dictInit(dict *ht, dictType *type, void *privDataPtr) { + _dictReset(ht); + ht->type = type; + ht->privdata = privDataPtr; + return DICT_OK; +} + +/* Expand or create the hashtable */ +static int dictExpand(dict *ht, unsigned long size) { + dict n; /* the new hashtable */ + unsigned long realsize = _dictNextPower(size), i; + + /* the size is invalid if it is smaller than the number of + * elements already inside the hashtable */ + if (ht->used > size) + return DICT_ERR; + + _dictInit(&n, ht->type, ht->privdata); + n.size = realsize; + n.sizemask = realsize-1; + n.table = calloc(realsize,sizeof(dictEntry*)); + + /* Copy all the elements from the old to the new table: + * note that if the old hash table is empty ht->size is zero, + * so dictExpand just creates an hash table. */ + n.used = ht->used; + for (i = 0; i < ht->size && ht->used > 0; i++) { + dictEntry *he, *nextHe; + + if (ht->table[i] == NULL) continue; + + /* For each hash entry on this slot... */ + he = ht->table[i]; + while(he) { + unsigned int h; + + nextHe = he->next; + /* Get the new element index */ + h = dictHashKey(ht, he->key) & n.sizemask; + he->next = n.table[h]; + n.table[h] = he; + ht->used--; + /* Pass to the next element */ + he = nextHe; + } + } + assert(ht->used == 0); + free(ht->table); + + /* Remap the new hashtable in the old */ + *ht = n; + return DICT_OK; +} + +/* Add an element to the target hash table */ +static int dictAdd(dict *ht, void *key, void *val) { + int index; + dictEntry *entry; + + /* Get the index of the new element, or -1 if + * the element already exists. */ + if ((index = _dictKeyIndex(ht, key)) == -1) + return DICT_ERR; + + /* Allocates the memory and stores key */ + entry = malloc(sizeof(*entry)); + entry->next = ht->table[index]; + ht->table[index] = entry; + + /* Set the hash entry fields. */ + dictSetHashKey(ht, entry, key); + dictSetHashVal(ht, entry, val); + ht->used++; + return DICT_OK; +} + +/* Add an element, discarding the old if the key already exists. + * Return 1 if the key was added from scratch, 0 if there was already an + * element with such key and dictReplace() just performed a value update + * operation. */ +static int dictReplace(dict *ht, void *key, void *val) { + dictEntry *entry, auxentry; + + /* Try to add the element. If the key + * does not exists dictAdd will suceed. */ + if (dictAdd(ht, key, val) == DICT_OK) + return 1; + /* It already exists, get the entry */ + entry = dictFind(ht, key); + /* Free the old value and set the new one */ + /* Set the new value and free the old one. Note that it is important + * to do that in this order, as the value may just be exactly the same + * as the previous one. In this context, think to reference counting, + * you want to increment (set), and then decrement (free), and not the + * reverse. */ + auxentry = *entry; + dictSetHashVal(ht, entry, val); + dictFreeEntryVal(ht, &auxentry); + return 0; +} + +/* Search and remove an element */ +static int dictDelete(dict *ht, const void *key) { + unsigned int h; + dictEntry *de, *prevde; + + if (ht->size == 0) + return DICT_ERR; + h = dictHashKey(ht, key) & ht->sizemask; + de = ht->table[h]; + + prevde = NULL; + while(de) { + if (dictCompareHashKeys(ht,key,de->key)) { + /* Unlink the element from the list */ + if (prevde) + prevde->next = de->next; + else + ht->table[h] = de->next; + + dictFreeEntryKey(ht,de); + dictFreeEntryVal(ht,de); + free(de); + ht->used--; + return DICT_OK; + } + prevde = de; + de = de->next; + } + return DICT_ERR; /* not found */ +} + +/* Destroy an entire hash table */ +static int _dictClear(dict *ht) { + unsigned long i; + + /* Free all the elements */ + for (i = 0; i < ht->size && ht->used > 0; i++) { + dictEntry *he, *nextHe; + + if ((he = ht->table[i]) == NULL) continue; + while(he) { + nextHe = he->next; + dictFreeEntryKey(ht, he); + dictFreeEntryVal(ht, he); + free(he); + ht->used--; + he = nextHe; + } + } + /* Free the table and the allocated cache structure */ + free(ht->table); + /* Re-initialize the table */ + _dictReset(ht); + return DICT_OK; /* never fails */ +} + +/* Clear & Release the hash table */ +static void dictRelease(dict *ht) { + _dictClear(ht); + free(ht); +} + +static dictEntry *dictFind(dict *ht, const void *key) { + dictEntry *he; + unsigned int h; + + if (ht->size == 0) return NULL; + h = dictHashKey(ht, key) & ht->sizemask; + he = ht->table[h]; + while(he) { + if (dictCompareHashKeys(ht, key, he->key)) + return he; + he = he->next; + } + return NULL; +} + +static dictIterator *dictGetIterator(dict *ht) { + dictIterator *iter = malloc(sizeof(*iter)); + + iter->ht = ht; + iter->index = -1; + iter->entry = NULL; + iter->nextEntry = NULL; + return iter; +} + +static dictEntry *dictNext(dictIterator *iter) { + while (1) { + if (iter->entry == NULL) { + iter->index++; + if (iter->index >= + (signed)iter->ht->size) break; + iter->entry = iter->ht->table[iter->index]; + } else { + iter->entry = iter->nextEntry; + } + if (iter->entry) { + /* We need to save the 'next' here, the iterator user + * may delete the entry we are returning. */ + iter->nextEntry = iter->entry->next; + return iter->entry; + } + } + return NULL; +} + +static void dictReleaseIterator(dictIterator *iter) { + free(iter); +} + +/* ------------------------- private functions ------------------------------ */ + +/* Expand the hash table if needed */ +static int _dictExpandIfNeeded(dict *ht) { + /* If the hash table is empty expand it to the intial size, + * if the table is "full" dobule its size. */ + if (ht->size == 0) + return dictExpand(ht, DICT_HT_INITIAL_SIZE); + if (ht->used == ht->size) + return dictExpand(ht, ht->size*2); + return DICT_OK; +} + +/* Our hash table capability is a power of two */ +static unsigned long _dictNextPower(unsigned long size) { + unsigned long i = DICT_HT_INITIAL_SIZE; + + if (size >= LONG_MAX) return LONG_MAX; + while(1) { + if (i >= size) + return i; + i *= 2; + } +} + +/* Returns the index of a free slot that can be populated with + * an hash entry for the given 'key'. + * If the key already exists, -1 is returned. */ +static int _dictKeyIndex(dict *ht, const void *key) { + unsigned int h; + dictEntry *he; + + /* Expand the hashtable if needed */ + if (_dictExpandIfNeeded(ht) == DICT_ERR) + return -1; + /* Compute the key hash value */ + h = dictHashKey(ht, key) & ht->sizemask; + /* Search if this slot does not already contain the given key */ + he = ht->table[h]; + while(he) { + if (dictCompareHashKeys(ht, key, he->key)) + return -1; + he = he->next; + } + return h; +} + diff --git a/data-server/external/hiredis/dict.h b/data-server/external/hiredis/dict.h new file mode 100755 index 0000000000..95fcd280e2 --- /dev/null +++ b/data-server/external/hiredis/dict.h @@ -0,0 +1,126 @@ +/* Hash table implementation. + * + * This file implements in memory hash tables with insert/del/replace/find/ + * get-random-element operations. Hash tables will auto resize if needed + * tables of power of two in size are used, collisions are handled by + * chaining. See the source code for more information... :) + * + * Copyright (c) 2006-2010, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DICT_H +#define __DICT_H + +#define DICT_OK 0 +#define DICT_ERR 1 + +/* Unused arguments generate annoying warnings... */ +#define DICT_NOTUSED(V) ((void) V) + +typedef struct dictEntry { + void *key; + void *val; + struct dictEntry *next; +} dictEntry; + +typedef struct dictType { + unsigned int (*hashFunction)(const void *key); + void *(*keyDup)(void *privdata, const void *key); + void *(*valDup)(void *privdata, const void *obj); + int (*keyCompare)(void *privdata, const void *key1, const void *key2); + void (*keyDestructor)(void *privdata, void *key); + void (*valDestructor)(void *privdata, void *obj); +} dictType; + +typedef struct dict { + dictEntry **table; + dictType *type; + unsigned long size; + unsigned long sizemask; + unsigned long used; + void *privdata; +} dict; + +typedef struct dictIterator { + dict *ht; + int index; + dictEntry *entry, *nextEntry; +} dictIterator; + +/* This is the initial size of every hash table */ +#define DICT_HT_INITIAL_SIZE 4 + +/* ------------------------------- Macros ------------------------------------*/ +#define dictFreeEntryVal(ht, entry) \ + if ((ht)->type->valDestructor) \ + (ht)->type->valDestructor((ht)->privdata, (entry)->val) + +#define dictSetHashVal(ht, entry, _val_) do { \ + if ((ht)->type->valDup) \ + entry->val = (ht)->type->valDup((ht)->privdata, _val_); \ + else \ + entry->val = (_val_); \ +} while(0) + +#define dictFreeEntryKey(ht, entry) \ + if ((ht)->type->keyDestructor) \ + (ht)->type->keyDestructor((ht)->privdata, (entry)->key) + +#define dictSetHashKey(ht, entry, _key_) do { \ + if ((ht)->type->keyDup) \ + entry->key = (ht)->type->keyDup((ht)->privdata, _key_); \ + else \ + entry->key = (_key_); \ +} while(0) + +#define dictCompareHashKeys(ht, key1, key2) \ + (((ht)->type->keyCompare) ? \ + (ht)->type->keyCompare((ht)->privdata, key1, key2) : \ + (key1) == (key2)) + +#define dictHashKey(ht, key) (ht)->type->hashFunction(key) + +#define dictGetEntryKey(he) ((he)->key) +#define dictGetEntryVal(he) ((he)->val) +#define dictSlots(ht) ((ht)->size) +#define dictSize(ht) ((ht)->used) + +/* API */ +static unsigned int dictGenHashFunction(const unsigned char *buf, int len); +static dict *dictCreate(dictType *type, void *privDataPtr); +static int dictExpand(dict *ht, unsigned long size); +static int dictAdd(dict *ht, void *key, void *val); +static int dictReplace(dict *ht, void *key, void *val); +static int dictDelete(dict *ht, const void *key); +static void dictRelease(dict *ht); +static dictEntry * dictFind(dict *ht, const void *key); +static dictIterator *dictGetIterator(dict *ht); +static dictEntry *dictNext(dictIterator *iter); +static void dictReleaseIterator(dictIterator *iter); + +#endif /* __DICT_H */ diff --git a/data-server/external/hiredis/fmacros.h b/data-server/external/hiredis/fmacros.h new file mode 100755 index 0000000000..799c12c1c0 --- /dev/null +++ b/data-server/external/hiredis/fmacros.h @@ -0,0 +1,20 @@ +#ifndef __HIREDIS_FMACRO_H +#define __HIREDIS_FMACRO_H + +#if !defined(_BSD_SOURCE) +#define _BSD_SOURCE +#endif + +#if defined(__sun__) +#define _POSIX_C_SOURCE 200112L +#elif defined(__linux__) +#define _XOPEN_SOURCE 600 +#else +#define _XOPEN_SOURCE +#endif + +#if __APPLE__ && __MACH__ +#define _OSX +#endif + +#endif diff --git a/data-server/external/hiredis/hiredis.c b/data-server/external/hiredis/hiredis.c new file mode 100755 index 0000000000..9b74b5b775 --- /dev/null +++ b/data-server/external/hiredis/hiredis.c @@ -0,0 +1,1322 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include +#include +#include +#include +#include +#include + +#include "hiredis.h" +#include "net.h" +#include "sds.h" + +static redisReply *createReplyObject(int type); +static void *createStringObject(const redisReadTask *task, char *str, size_t len); +static void *createArrayObject(const redisReadTask *task, int elements); +static void *createIntegerObject(const redisReadTask *task, long long value); +static void *createNilObject(const redisReadTask *task); + +/* Default set of functions to build the reply. Keep in mind that such a + * function returning NULL is interpreted as OOM. */ +static redisReplyObjectFunctions defaultFunctions = { + createStringObject, + createArrayObject, + createIntegerObject, + createNilObject, + freeReplyObject +}; + +/* Create a reply object */ +static redisReply *createReplyObject(int type) { + redisReply *r = calloc(1,sizeof(*r)); + + if (r == NULL) + return NULL; + + r->type = type; + return r; +} + +/* Free a reply object */ +void freeReplyObject(void *reply) { + redisReply *r = reply; + size_t j; + + switch(r->type) { + case REDIS_REPLY_INTEGER: + break; /* Nothing to free */ + case REDIS_REPLY_ARRAY: + if (r->element != NULL) { + for (j = 0; j < r->elements; j++) + if (r->element[j] != NULL) + freeReplyObject(r->element[j]); + free(r->element); + } + break; + case REDIS_REPLY_ERROR: + case REDIS_REPLY_STATUS: + case REDIS_REPLY_STRING: + if (r->str != NULL) + free(r->str); + break; + } + free(r); +} + +static void *createStringObject(const redisReadTask *task, char *str, size_t len) { + redisReply *r, *parent; + char *buf; + + r = createReplyObject(task->type); + if (r == NULL) + return NULL; + + buf = malloc(len+1); + if (buf == NULL) { + freeReplyObject(r); + return NULL; + } + + assert(task->type == REDIS_REPLY_ERROR || + task->type == REDIS_REPLY_STATUS || + task->type == REDIS_REPLY_STRING); + + /* Copy string value */ + memcpy(buf,str,len); + buf[len] = '\0'; + r->str = buf; + r->len = len; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY); + parent->element[task->idx] = r; + } + return r; +} + +static void *createArrayObject(const redisReadTask *task, int elements) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_ARRAY); + if (r == NULL) + return NULL; + + if (elements > 0) { + r->element = calloc(elements,sizeof(redisReply*)); + if (r->element == NULL) { + freeReplyObject(r); + return NULL; + } + } + + r->elements = elements; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY); + parent->element[task->idx] = r; + } + return r; +} + +static void *createIntegerObject(const redisReadTask *task, long long value) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_INTEGER); + if (r == NULL) + return NULL; + + r->integer = value; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY); + parent->element[task->idx] = r; + } + return r; +} + +static void *createNilObject(const redisReadTask *task) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_NIL); + if (r == NULL) + return NULL; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY); + parent->element[task->idx] = r; + } + return r; +} + +static void __redisReaderSetError(redisReader *r, int type, const char *str) { + size_t len; + + if (r->reply != NULL && r->fn && r->fn->freeObject) { + r->fn->freeObject(r->reply); + r->reply = NULL; + } + + /* Clear input buffer on errors. */ + if (r->buf != NULL) { + sdsfree(r->buf); + r->buf = NULL; + r->pos = r->len = 0; + } + + /* Reset task stack. */ + r->ridx = -1; + + /* Set error. */ + r->err = type; + len = strlen(str); + len = len < (sizeof(r->errstr)-1) ? len : (sizeof(r->errstr)-1); + memcpy(r->errstr,str,len); + r->errstr[len] = '\0'; +} + +static size_t chrtos(char *buf, size_t size, char byte) { + size_t len = 0; + + switch(byte) { + case '\\': + case '"': + len = snprintf(buf,size,"\"\\%c\"",byte); + break; + case '\n': len = snprintf(buf,size,"\"\\n\""); break; + case '\r': len = snprintf(buf,size,"\"\\r\""); break; + case '\t': len = snprintf(buf,size,"\"\\t\""); break; + case '\a': len = snprintf(buf,size,"\"\\a\""); break; + case '\b': len = snprintf(buf,size,"\"\\b\""); break; + default: + if (isprint(byte)) + len = snprintf(buf,size,"\"%c\"",byte); + else + len = snprintf(buf,size,"\"\\x%02x\"",(unsigned char)byte); + break; + } + + return len; +} + +static void __redisReaderSetErrorProtocolByte(redisReader *r, char byte) { + char cbuf[8], sbuf[128]; + + chrtos(cbuf,sizeof(cbuf),byte); + snprintf(sbuf,sizeof(sbuf), + "Protocol error, got %s as reply type byte", cbuf); + __redisReaderSetError(r,REDIS_ERR_PROTOCOL,sbuf); +} + +static void __redisReaderSetErrorOOM(redisReader *r) { + __redisReaderSetError(r,REDIS_ERR_OOM,"Out of memory"); +} + +static char *readBytes(redisReader *r, unsigned int bytes) { + char *p; + if (r->len-r->pos >= bytes) { + p = r->buf+r->pos; + r->pos += bytes; + return p; + } + return NULL; +} + +/* Find pointer to \r\n. */ +static char *seekNewline(char *s, size_t len) { + int pos = 0; + int _len = len-1; + + /* Position should be < len-1 because the character at "pos" should be + * followed by a \n. Note that strchr cannot be used because it doesn't + * allow to search a limited length and the buffer that is being searched + * might not have a trailing NULL character. */ + while (pos < _len) { + while(pos < _len && s[pos] != '\r') pos++; + if (s[pos] != '\r') { + /* Not found. */ + return NULL; + } else { + if (s[pos+1] == '\n') { + /* Found. */ + return s+pos; + } else { + /* Continue searching. */ + pos++; + } + } + } + return NULL; +} + +/* Read a long long value starting at *s, under the assumption that it will be + * terminated by \r\n. Ambiguously returns -1 for unexpected input. */ +static long long readLongLong(char *s) { + long long v = 0; + int dec, mult = 1; + char c; + + if (*s == '-') { + mult = -1; + s++; + } else if (*s == '+') { + mult = 1; + s++; + } + + while ((c = *(s++)) != '\r') { + dec = c - '0'; + if (dec >= 0 && dec < 10) { + v *= 10; + v += dec; + } else { + /* Should not happen... */ + return -1; + } + } + + return mult*v; +} + +static char *readLine(redisReader *r, int *_len) { + char *p, *s; + int len; + + p = r->buf+r->pos; + s = seekNewline(p,(r->len-r->pos)); + if (s != NULL) { + len = s-(r->buf+r->pos); + r->pos += len+2; /* skip \r\n */ + if (_len) *_len = len; + return p; + } + return NULL; +} + +static void moveToNextTask(redisReader *r) { + redisReadTask *cur, *prv; + while (r->ridx >= 0) { + /* Return a.s.a.p. when the stack is now empty. */ + if (r->ridx == 0) { + r->ridx--; + return; + } + + cur = &(r->rstack[r->ridx]); + prv = &(r->rstack[r->ridx-1]); + assert(prv->type == REDIS_REPLY_ARRAY); + if (cur->idx == prv->elements-1) { + r->ridx--; + } else { + /* Reset the type because the next item can be anything */ + assert(cur->idx < prv->elements); + cur->type = -1; + cur->elements = -1; + cur->idx++; + return; + } + } +} + +static int processLineItem(redisReader *r) { + redisReadTask *cur = &(r->rstack[r->ridx]); + void *obj; + char *p; + int len; + + if ((p = readLine(r,&len)) != NULL) { + if (cur->type == REDIS_REPLY_INTEGER) { + if (r->fn && r->fn->createInteger) + obj = r->fn->createInteger(cur,readLongLong(p)); + else + obj = (void*)REDIS_REPLY_INTEGER; + } else { + /* Type will be error or status. */ + if (r->fn && r->fn->createString) + obj = r->fn->createString(cur,p,len); + else + obj = (void*)(size_t)(cur->type); + } + + if (obj == NULL) { + __redisReaderSetErrorOOM(r); + return REDIS_ERR; + } + + /* Set reply if this is the root object. */ + if (r->ridx == 0) r->reply = obj; + moveToNextTask(r); + return REDIS_OK; + } + + return REDIS_ERR; +} + +static int processBulkItem(redisReader *r) { + redisReadTask *cur = &(r->rstack[r->ridx]); + void *obj = NULL; + char *p, *s; + long len; + unsigned long bytelen; + int success = 0; + + p = r->buf+r->pos; + s = seekNewline(p,r->len-r->pos); + if (s != NULL) { + p = r->buf+r->pos; + bytelen = s-(r->buf+r->pos)+2; /* include \r\n */ + len = readLongLong(p); + + if (len < 0) { + /* The nil object can always be created. */ + if (r->fn && r->fn->createNil) + obj = r->fn->createNil(cur); + else + obj = (void*)REDIS_REPLY_NIL; + success = 1; + } else { + /* Only continue when the buffer contains the entire bulk item. */ + bytelen += len+2; /* include \r\n */ + if (r->pos+bytelen <= r->len) { + if (r->fn && r->fn->createString) + obj = r->fn->createString(cur,s+2,len); + else + obj = (void*)REDIS_REPLY_STRING; + success = 1; + } + } + + /* Proceed when obj was created. */ + if (success) { + if (obj == NULL) { + __redisReaderSetErrorOOM(r); + return REDIS_ERR; + } + + r->pos += bytelen; + + /* Set reply if this is the root object. */ + if (r->ridx == 0) r->reply = obj; + moveToNextTask(r); + return REDIS_OK; + } + } + + return REDIS_ERR; +} + +static int processMultiBulkItem(redisReader *r) { + redisReadTask *cur = &(r->rstack[r->ridx]); + void *obj; + char *p; + long elements; + int root = 0; + + /* Set error for nested multi bulks with depth > 7 */ + if (r->ridx == 8) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "No support for nested multi bulk replies with depth > 7"); + return REDIS_ERR; + } + + if ((p = readLine(r,NULL)) != NULL) { + elements = readLongLong(p); + root = (r->ridx == 0); + + if (elements == -1) { + if (r->fn && r->fn->createNil) + obj = r->fn->createNil(cur); + else + obj = (void*)REDIS_REPLY_NIL; + + if (obj == NULL) { + __redisReaderSetErrorOOM(r); + return REDIS_ERR; + } + + moveToNextTask(r); + } else { + if (r->fn && r->fn->createArray) + obj = r->fn->createArray(cur,elements); + else + obj = (void*)REDIS_REPLY_ARRAY; + + if (obj == NULL) { + __redisReaderSetErrorOOM(r); + return REDIS_ERR; + } + + /* Modify task stack when there are more than 0 elements. */ + if (elements > 0) { + cur->elements = elements; + cur->obj = obj; + r->ridx++; + r->rstack[r->ridx].type = -1; + r->rstack[r->ridx].elements = -1; + r->rstack[r->ridx].idx = 0; + r->rstack[r->ridx].obj = NULL; + r->rstack[r->ridx].parent = cur; + r->rstack[r->ridx].privdata = r->privdata; + } else { + moveToNextTask(r); + } + } + + /* Set reply if this is the root object. */ + if (root) r->reply = obj; + return REDIS_OK; + } + + return REDIS_ERR; +} + +static int processItem(redisReader *r) { + redisReadTask *cur = &(r->rstack[r->ridx]); + char *p; + + /* check if we need to read type */ + if (cur->type < 0) { + if ((p = readBytes(r,1)) != NULL) { + switch (p[0]) { + case '-': + cur->type = REDIS_REPLY_ERROR; + break; + case '+': + cur->type = REDIS_REPLY_STATUS; + break; + case ':': + cur->type = REDIS_REPLY_INTEGER; + break; + case '$': + cur->type = REDIS_REPLY_STRING; + break; + case '*': + cur->type = REDIS_REPLY_ARRAY; + break; + default: + __redisReaderSetErrorProtocolByte(r,*p); + return REDIS_ERR; + } + } else { + /* could not consume 1 byte */ + return REDIS_ERR; + } + } + + /* process typed item */ + switch(cur->type) { + case REDIS_REPLY_ERROR: + case REDIS_REPLY_STATUS: + case REDIS_REPLY_INTEGER: + return processLineItem(r); + case REDIS_REPLY_STRING: + return processBulkItem(r); + case REDIS_REPLY_ARRAY: + return processMultiBulkItem(r); + default: + assert(NULL); + return REDIS_ERR; /* Avoid warning. */ + } +} + +redisReader *redisReaderCreate(void) { + redisReader *r; + + r = calloc(sizeof(redisReader),1); + if (r == NULL) + return NULL; + + r->err = 0; + r->errstr[0] = '\0'; + r->fn = &defaultFunctions; + r->buf = sdsempty(); + r->maxbuf = REDIS_READER_MAX_BUF; + if (r->buf == NULL) { + free(r); + return NULL; + } + + r->ridx = -1; + return r; +} + +void redisReaderFree(redisReader *r) { + if (r->reply != NULL && r->fn && r->fn->freeObject) + r->fn->freeObject(r->reply); + if (r->buf != NULL) + sdsfree(r->buf); + free(r); +} + +int redisReaderFeed(redisReader *r, const char *buf, size_t len) { + sds newbuf; + + /* Return early when this reader is in an erroneous state. */ + if (r->err) + return REDIS_ERR; + + /* Copy the provided buffer. */ + if (buf != NULL && len >= 1) { + /* Destroy internal buffer when it is empty and is quite large. */ + if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) { + sdsfree(r->buf); + r->buf = sdsempty(); + r->pos = 0; + + /* r->buf should not be NULL since we just free'd a larger one. */ + assert(r->buf != NULL); + } + + newbuf = sdscatlen(r->buf,buf,len); + if (newbuf == NULL) { + __redisReaderSetErrorOOM(r); + return REDIS_ERR; + } + + r->buf = newbuf; + r->len = sdslen(r->buf); + } + + return REDIS_OK; +} + +int redisReaderGetReply(redisReader *r, void **reply) { + /* Default target pointer to NULL. */ + if (reply != NULL) + *reply = NULL; + + /* Return early when this reader is in an erroneous state. */ + if (r->err) + return REDIS_ERR; + + /* When the buffer is empty, there will never be a reply. */ + if (r->len == 0) + return REDIS_OK; + + /* Set first item to process when the stack is empty. */ + if (r->ridx == -1) { + r->rstack[0].type = -1; + r->rstack[0].elements = -1; + r->rstack[0].idx = -1; + r->rstack[0].obj = NULL; + r->rstack[0].parent = NULL; + r->rstack[0].privdata = r->privdata; + r->ridx = 0; + } + + /* Process items in reply. */ + while (r->ridx >= 0) + if (processItem(r) != REDIS_OK) + break; + + /* Return ASAP when an error occurred. */ + if (r->err) + return REDIS_ERR; + + /* Discard part of the buffer when we've consumed at least 1k, to avoid + * doing unnecessary calls to memmove() in sds.c. */ + if (r->pos >= 1024) { + r->buf = sdsrange(r->buf,r->pos,-1); + r->pos = 0; + r->len = sdslen(r->buf); + } + + /* Emit a reply when there is one. */ + if (r->ridx == -1) { + if (reply != NULL) + *reply = r->reply; + r->reply = NULL; + } + return REDIS_OK; +} + +/* Calculate the number of bytes needed to represent an integer as string. */ +static int intlen(int i) { + int len = 0; + if (i < 0) { + len++; + i = -i; + } + do { + len++; + i /= 10; + } while(i); + return len; +} + +/* Helper that calculates the bulk length given a certain string length. */ +static size_t bulklen(size_t len) { + return 1+intlen(len)+2+len+2; +} + +int redisvFormatCommand(char **target, const char *format, va_list ap) { + const char *c = format; + char *cmd = NULL; /* final command */ + int pos; /* position in final command */ + sds curarg, newarg; /* current argument */ + int touched = 0; /* was the current argument touched? */ + char **curargv = NULL, **newargv = NULL; + int argc = 0; + int totlen = 0; + int j; + + /* Abort if there is not target to set */ + if (target == NULL) + return -1; + + /* Build the command string accordingly to protocol */ + curarg = sdsempty(); + if (curarg == NULL) + return -1; + + while(*c != '\0') { + if (*c != '%' || c[1] == '\0') { + if (*c == ' ') { + if (touched) { + newargv = realloc(curargv,sizeof(char*)*(argc+1)); + if (newargv == NULL) goto err; + curargv = newargv; + curargv[argc++] = curarg; + totlen += bulklen(sdslen(curarg)); + + /* curarg is put in argv so it can be overwritten. */ + curarg = sdsempty(); + if (curarg == NULL) goto err; + touched = 0; + } + } else { + newarg = sdscatlen(curarg,c,1); + if (newarg == NULL) goto err; + curarg = newarg; + touched = 1; + } + } else { + char *arg; + size_t size; + + /* Set newarg so it can be checked even if it is not touched. */ + newarg = curarg; + + switch(c[1]) { + case 's': + arg = va_arg(ap,char*); + size = strlen(arg); + if (size > 0) + newarg = sdscatlen(curarg,arg,size); + break; + case 'b': + arg = va_arg(ap,char*); + size = va_arg(ap,size_t); + if (size > 0) + newarg = sdscatlen(curarg,arg,size); + break; + case '%': + newarg = sdscat(curarg,"%"); + break; + default: + /* Try to detect printf format */ + { + static const char intfmts[] = "diouxX"; + char _format[16]; + const char *_p = c+1; + size_t _l = 0; + va_list _cpy; + + /* Flags */ + if (*_p != '\0' && *_p == '#') _p++; + if (*_p != '\0' && *_p == '0') _p++; + if (*_p != '\0' && *_p == '-') _p++; + if (*_p != '\0' && *_p == ' ') _p++; + if (*_p != '\0' && *_p == '+') _p++; + + /* Field width */ + while (*_p != '\0' && isdigit(*_p)) _p++; + + /* Precision */ + if (*_p == '.') { + _p++; + while (*_p != '\0' && isdigit(*_p)) _p++; + } + + /* Copy va_list before consuming with va_arg */ + va_copy(_cpy,ap); + + /* Integer conversion (without modifiers) */ + if (strchr(intfmts,*_p) != NULL) { + va_arg(ap,int); + goto fmt_valid; + } + + /* Double conversion (without modifiers) */ + if (strchr("eEfFgGaA",*_p) != NULL) { + va_arg(ap,double); + goto fmt_valid; + } + + /* Size: char */ + if (_p[0] == 'h' && _p[1] == 'h') { + _p += 2; + if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { + va_arg(ap,int); /* char gets promoted to int */ + goto fmt_valid; + } + goto fmt_invalid; + } + + /* Size: short */ + if (_p[0] == 'h') { + _p += 1; + if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { + va_arg(ap,int); /* short gets promoted to int */ + goto fmt_valid; + } + goto fmt_invalid; + } + + /* Size: long long */ + if (_p[0] == 'l' && _p[1] == 'l') { + _p += 2; + if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { + va_arg(ap,long long); + goto fmt_valid; + } + goto fmt_invalid; + } + + /* Size: long */ + if (_p[0] == 'l') { + _p += 1; + if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { + va_arg(ap,long); + goto fmt_valid; + } + goto fmt_invalid; + } + + fmt_invalid: + va_end(_cpy); + goto err; + + fmt_valid: + _l = (_p+1)-c; + if (_l < sizeof(_format)-2) { + memcpy(_format,c,_l); + _format[_l] = '\0'; + newarg = sdscatvprintf(curarg,_format,_cpy); + + /* Update current position (note: outer blocks + * increment c twice so compensate here) */ + c = _p-1; + } + + va_end(_cpy); + break; + } + } + + if (newarg == NULL) goto err; + curarg = newarg; + + touched = 1; + c++; + } + c++; + } + + /* Add the last argument if needed */ + if (touched) { + newargv = realloc(curargv,sizeof(char*)*(argc+1)); + if (newargv == NULL) goto err; + curargv = newargv; + curargv[argc++] = curarg; + totlen += bulklen(sdslen(curarg)); + } else { + sdsfree(curarg); + } + + /* Clear curarg because it was put in curargv or was free'd. */ + curarg = NULL; + + /* Add bytes needed to hold multi bulk count */ + totlen += 1+intlen(argc)+2; + + /* Build the command at protocol level */ + cmd = malloc(totlen+1); + if (cmd == NULL) goto err; + + pos = sprintf(cmd,"*%d\r\n",argc); + for (j = 0; j < argc; j++) { + pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j])); + memcpy(cmd+pos,curargv[j],sdslen(curargv[j])); + pos += sdslen(curargv[j]); + sdsfree(curargv[j]); + cmd[pos++] = '\r'; + cmd[pos++] = '\n'; + } + assert(pos == totlen); + cmd[pos] = '\0'; + + free(curargv); + *target = cmd; + return totlen; + +err: + while(argc--) + sdsfree(curargv[argc]); + free(curargv); + + if (curarg != NULL) + sdsfree(curarg); + + /* No need to check cmd since it is the last statement that can fail, + * but do it anyway to be as defensive as possible. */ + if (cmd != NULL) + free(cmd); + + return -1; +} + +/* Format a command according to the Redis protocol. This function + * takes a format similar to printf: + * + * %s represents a C null terminated string you want to interpolate + * %b represents a binary safe string + * + * When using %b you need to provide both the pointer to the string + * and the length in bytes as a size_t. Examples: + * + * len = redisFormatCommand(target, "GET %s", mykey); + * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen); + */ +int redisFormatCommand(char **target, const char *format, ...) { + va_list ap; + int len; + va_start(ap,format); + len = redisvFormatCommand(target,format,ap); + va_end(ap); + return len; +} + +/* Format a command according to the Redis protocol. This function takes the + * number of arguments, an array with arguments and an array with their + * lengths. If the latter is set to NULL, strlen will be used to compute the + * argument lengths. + */ +int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) { + char *cmd = NULL; /* final command */ + int pos; /* position in final command */ + size_t len; + int totlen, j; + + /* Calculate number of bytes needed for the command */ + totlen = 1+intlen(argc)+2; + for (j = 0; j < argc; j++) { + len = argvlen ? argvlen[j] : strlen(argv[j]); + totlen += bulklen(len); + } + + /* Build the command at protocol level */ + cmd = malloc(totlen+1); + if (cmd == NULL) + return -1; + + pos = sprintf(cmd,"*%d\r\n",argc); + for (j = 0; j < argc; j++) { + len = argvlen ? argvlen[j] : strlen(argv[j]); + pos += sprintf(cmd+pos,"$%zu\r\n",len); + memcpy(cmd+pos,argv[j],len); + pos += len; + cmd[pos++] = '\r'; + cmd[pos++] = '\n'; + } + assert(pos == totlen); + cmd[pos] = '\0'; + + *target = cmd; + return totlen; +} + +void __redisSetError(redisContext *c, int type, const char *str) { + size_t len; + + c->err = type; + if (str != NULL) { + len = strlen(str); + len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1); + memcpy(c->errstr,str,len); + c->errstr[len] = '\0'; + } else { + /* Only REDIS_ERR_IO may lack a description! */ + assert(type == REDIS_ERR_IO); + strerror_r(errno,c->errstr,sizeof(c->errstr)); + } +} + +static redisContext *redisContextInit(void) { + redisContext *c; + + c = calloc(1,sizeof(redisContext)); + if (c == NULL) + return NULL; + + c->err = 0; + c->errstr[0] = '\0'; + c->obuf = sdsempty(); + c->reader = redisReaderCreate(); + return c; +} + +void redisFree(redisContext *c) { + if (c->fd > 0) + close(c->fd); + if (c->obuf != NULL) + sdsfree(c->obuf); + if (c->reader != NULL) + redisReaderFree(c->reader); + free(c); +} + +/* Connect to a Redis instance. On error the field error in the returned + * context will be set to the return value of the error function. + * When no set of reply functions is given, the default set will be used. */ +redisContext *redisConnect(const char *ip, int port) { + redisContext *c; + + c = redisContextInit(); + if (c == NULL) + return NULL; + + c->flags |= REDIS_BLOCK; + redisContextConnectTcp(c,ip,port,NULL); + return c; +} + +redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { + redisContext *c; + + c = redisContextInit(); + if (c == NULL) + return NULL; + + c->flags |= REDIS_BLOCK; + redisContextConnectTcp(c,ip,port,&tv); + return c; +} + +redisContext *redisConnectNonBlock(const char *ip, int port) { + redisContext *c; + + c = redisContextInit(); + if (c == NULL) + return NULL; + + c->flags &= ~REDIS_BLOCK; + redisContextConnectTcp(c,ip,port,NULL); + return c; +} + +redisContext *redisConnectUnix(const char *path) { + redisContext *c; + + c = redisContextInit(); + if (c == NULL) + return NULL; + + c->flags |= REDIS_BLOCK; + redisContextConnectUnix(c,path,NULL); + return c; +} + +redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) { + redisContext *c; + + c = redisContextInit(); + if (c == NULL) + return NULL; + + c->flags |= REDIS_BLOCK; + redisContextConnectUnix(c,path,&tv); + return c; +} + +redisContext *redisConnectUnixNonBlock(const char *path) { + redisContext *c; + + c = redisContextInit(); + if (c == NULL) + return NULL; + + c->flags &= ~REDIS_BLOCK; + redisContextConnectUnix(c,path,NULL); + return c; +} + +/* Set read/write timeout on a blocking socket. */ +int redisSetTimeout(redisContext *c, const struct timeval tv) { + if (c->flags & REDIS_BLOCK) + return redisContextSetTimeout(c,tv); + return REDIS_ERR; +} + +/* Enable connection KeepAlive. */ +int redisEnableKeepAlive(redisContext *c) { + if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK) + return REDIS_ERR; + return REDIS_OK; +} + +/* Use this function to handle a read event on the descriptor. It will try + * and read some bytes from the socket and feed them to the reply parser. + * + * After this function is called, you may use redisContextReadReply to + * see if there is a reply available. */ +int redisBufferRead(redisContext *c) { + char buf[1024*16]; + int nread; + + /* Return early when the context has seen an error. */ + if (c->err) + return REDIS_ERR; + + nread = read(c->fd,buf,sizeof(buf)); + if (nread == -1) { + if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { + /* Try again later */ + } else { + __redisSetError(c,REDIS_ERR_IO,NULL); + return REDIS_ERR; + } + } else if (nread == 0) { + __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection"); + return REDIS_ERR; + } else { + if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) { + __redisSetError(c,c->reader->err,c->reader->errstr); + return REDIS_ERR; + } + } + return REDIS_OK; +} + +/* Write the output buffer to the socket. + * + * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was + * succesfully written to the socket. When the buffer is empty after the + * write operation, "done" is set to 1 (if given). + * + * Returns REDIS_ERR if an error occured trying to write and sets + * c->errstr to hold the appropriate error string. + */ +int redisBufferWrite(redisContext *c, int *done) { + int nwritten; + + /* Return early when the context has seen an error. */ + if (c->err) + return REDIS_ERR; + + if (sdslen(c->obuf) > 0) { + nwritten = write(c->fd,c->obuf,sdslen(c->obuf)); + if (nwritten == -1) { + if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { + /* Try again later */ + } else { + __redisSetError(c,REDIS_ERR_IO,NULL); + return REDIS_ERR; + } + } else if (nwritten > 0) { + if (nwritten == (signed)sdslen(c->obuf)) { + sdsfree(c->obuf); + c->obuf = sdsempty(); + } else { + c->obuf = sdsrange(c->obuf,nwritten,-1); + } + } + } + if (done != NULL) *done = (sdslen(c->obuf) == 0); + return REDIS_OK; +} + +/* Internal helper function to try and get a reply from the reader, + * or set an error in the context otherwise. */ +int redisGetReplyFromReader(redisContext *c, void **reply) { + if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { + __redisSetError(c,c->reader->err,c->reader->errstr); + return REDIS_ERR; + } + return REDIS_OK; +} + +int redisGetReply(redisContext *c, void **reply) { + int wdone = 0; + void *aux = NULL; + + /* Try to read pending replies */ + if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) + return REDIS_ERR; + + /* For the blocking context, flush output buffer and read reply */ + if (aux == NULL && c->flags & REDIS_BLOCK) { + /* Write until done */ + do { + if (redisBufferWrite(c,&wdone) == REDIS_ERR) + return REDIS_ERR; + } while (!wdone); + + /* Read until there is a reply */ + do { + if (redisBufferRead(c) == REDIS_ERR) + return REDIS_ERR; + if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) + return REDIS_ERR; + } while (aux == NULL); + } + + /* Set reply object */ + if (reply != NULL) *reply = aux; + return REDIS_OK; +} + + +/* Helper function for the redisAppendCommand* family of functions. + * + * Write a formatted command to the output buffer. When this family + * is used, you need to call redisGetReply yourself to retrieve + * the reply (or replies in pub/sub). + */ +int __redisAppendCommand(redisContext *c, char *cmd, size_t len) { + sds newbuf; + + newbuf = sdscatlen(c->obuf,cmd,len); + if (newbuf == NULL) { + __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); + return REDIS_ERR; + } + + c->obuf = newbuf; + return REDIS_OK; +} + +int redisvAppendCommand(redisContext *c, const char *format, va_list ap) { + char *cmd; + int len; + + len = redisvFormatCommand(&cmd,format,ap); + if (len == -1) { + __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); + return REDIS_ERR; + } + + if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { + free(cmd); + return REDIS_ERR; + } + + free(cmd); + return REDIS_OK; +} + +int redisAppendCommand(redisContext *c, const char *format, ...) { + va_list ap; + int ret; + + va_start(ap,format); + ret = redisvAppendCommand(c,format,ap); + va_end(ap); + return ret; +} + +int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { + char *cmd; + int len; + + len = redisFormatCommandArgv(&cmd,argc,argv,argvlen); + if (len == -1) { + __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); + return REDIS_ERR; + } + + if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { + free(cmd); + return REDIS_ERR; + } + + free(cmd); + return REDIS_OK; +} + +/* Helper function for the redisCommand* family of functions. + * + * Write a formatted command to the output buffer. If the given context is + * blocking, immediately read the reply into the "reply" pointer. When the + * context is non-blocking, the "reply" pointer will not be used and the + * command is simply appended to the write buffer. + * + * Returns the reply when a reply was succesfully retrieved. Returns NULL + * otherwise. When NULL is returned in a blocking context, the error field + * in the context will be set. + */ +static void *__redisBlockForReply(redisContext *c) { + void *reply; + + if (c->flags & REDIS_BLOCK) { + if (redisGetReply(c,&reply) != REDIS_OK) + return NULL; + return reply; + } + return NULL; +} + +void *redisvCommand(redisContext *c, const char *format, va_list ap) { + if (redisvAppendCommand(c,format,ap) != REDIS_OK) + return NULL; + return __redisBlockForReply(c); +} + +void *redisCommand(redisContext *c, const char *format, ...) { + va_list ap; + void *reply = NULL; + va_start(ap,format); + reply = redisvCommand(c,format,ap); + va_end(ap); + return reply; +} + +void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { + if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK) + return NULL; + return __redisBlockForReply(c); +} diff --git a/data-server/external/hiredis/hiredis.h b/data-server/external/hiredis/hiredis.h new file mode 100755 index 0000000000..c65098b740 --- /dev/null +++ b/data-server/external/hiredis/hiredis.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2009-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __HIREDIS_H +#define __HIREDIS_H +#include /* for size_t */ +#include /* for va_list */ +#include /* for struct timeval */ + +#define HIREDIS_MAJOR 0 +#define HIREDIS_MINOR 11 +#define HIREDIS_PATCH 0 + +#define REDIS_ERR -1 +#define REDIS_OK 0 + +/* When an error occurs, the err flag in a context is set to hold the type of + * error that occured. REDIS_ERR_IO means there was an I/O error and you + * should use the "errno" variable to find out what is wrong. + * For other values, the "errstr" field will hold a description. */ +#define REDIS_ERR_IO 1 /* Error in read or write */ +#define REDIS_ERR_EOF 3 /* End of file */ +#define REDIS_ERR_PROTOCOL 4 /* Protocol error */ +#define REDIS_ERR_OOM 5 /* Out of memory */ +#define REDIS_ERR_OTHER 2 /* Everything else... */ + +/* Connection type can be blocking or non-blocking and is set in the + * least significant bit of the flags field in redisContext. */ +#define REDIS_BLOCK 0x1 + +/* Connection may be disconnected before being free'd. The second bit + * in the flags field is set when the context is connected. */ +#define REDIS_CONNECTED 0x2 + +/* The async API might try to disconnect cleanly and flush the output + * buffer and read all subsequent replies before disconnecting. + * This flag means no new commands can come in and the connection + * should be terminated once all replies have been read. */ +#define REDIS_DISCONNECTING 0x4 + +/* Flag specific to the async API which means that the context should be clean + * up as soon as possible. */ +#define REDIS_FREEING 0x8 + +/* Flag that is set when an async callback is executed. */ +#define REDIS_IN_CALLBACK 0x10 + +/* Flag that is set when the async context has one or more subscriptions. */ +#define REDIS_SUBSCRIBED 0x20 + +/* Flag that is set when monitor mode is active */ +#define REDIS_MONITORING 0x40 + +#define REDIS_REPLY_STRING 1 +#define REDIS_REPLY_ARRAY 2 +#define REDIS_REPLY_INTEGER 3 +#define REDIS_REPLY_NIL 4 +#define REDIS_REPLY_STATUS 5 +#define REDIS_REPLY_ERROR 6 + +#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */ + +#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is the reply object returned by redisCommand() */ +typedef struct redisReply { + int type; /* REDIS_REPLY_* */ + long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ + int len; /* Length of string */ + char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ + size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ + struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ +} redisReply; + +typedef struct redisReadTask { + int type; + int elements; /* number of elements in multibulk container */ + int idx; /* index in parent (array) object */ + void *obj; /* holds user-generated value for a read task */ + struct redisReadTask *parent; /* parent task */ + void *privdata; /* user-settable arbitrary field */ +} redisReadTask; + +typedef struct redisReplyObjectFunctions { + void *(*createString)(const redisReadTask*, char*, size_t); + void *(*createArray)(const redisReadTask*, int); + void *(*createInteger)(const redisReadTask*, long long); + void *(*createNil)(const redisReadTask*); + void (*freeObject)(void*); +} redisReplyObjectFunctions; + +/* State for the protocol parser */ +typedef struct redisReader { + int err; /* Error flags, 0 when there is no error */ + char errstr[128]; /* String representation of error when applicable */ + + char *buf; /* Read buffer */ + size_t pos; /* Buffer cursor */ + size_t len; /* Buffer length */ + size_t maxbuf; /* Max length of unused buffer */ + + redisReadTask rstack[9]; + int ridx; /* Index of current read task */ + void *reply; /* Temporary reply pointer */ + + redisReplyObjectFunctions *fn; + void *privdata; +} redisReader; + +/* Public API for the protocol parser. */ +redisReader *redisReaderCreate(void); +void redisReaderFree(redisReader *r); +int redisReaderFeed(redisReader *r, const char *buf, size_t len); +int redisReaderGetReply(redisReader *r, void **reply); + +/* Backwards compatibility, can be removed on big version bump. */ +#define redisReplyReaderCreate redisReaderCreate +#define redisReplyReaderFree redisReaderFree +#define redisReplyReaderFeed redisReaderFeed +#define redisReplyReaderGetReply redisReaderGetReply +#define redisReplyReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p)) +#define redisReplyReaderGetObject(_r) (((redisReader*)(_r))->reply) +#define redisReplyReaderGetError(_r) (((redisReader*)(_r))->errstr) + +/* Function to free the reply objects hiredis returns by default. */ +void freeReplyObject(void *reply); + +/* Functions to format a command according to the protocol. */ +int redisvFormatCommand(char **target, const char *format, va_list ap); +int redisFormatCommand(char **target, const char *format, ...); +int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen); + +/* Context for a connection to Redis */ +typedef struct redisContext { + int err; /* Error flags, 0 when there is no error */ + char errstr[128]; /* String representation of error when applicable */ + int fd; + int flags; + char *obuf; /* Write buffer */ + redisReader *reader; /* Protocol reader */ +} redisContext; + +redisContext *redisConnect(const char *ip, int port); +redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); +redisContext *redisConnectNonBlock(const char *ip, int port); +redisContext *redisConnectUnix(const char *path); +redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); +redisContext *redisConnectUnixNonBlock(const char *path); +int redisSetTimeout(redisContext *c, const struct timeval tv); +int redisEnableKeepAlive(redisContext *c); +void redisFree(redisContext *c); +int redisBufferRead(redisContext *c); +int redisBufferWrite(redisContext *c, int *done); + +/* In a blocking context, this function first checks if there are unconsumed + * replies to return and returns one if so. Otherwise, it flushes the output + * buffer to the socket and reads until it has a reply. In a non-blocking + * context, it will return unconsumed replies until there are no more. */ +int redisGetReply(redisContext *c, void **reply); +int redisGetReplyFromReader(redisContext *c, void **reply); + +/* Write a command to the output buffer. Use these functions in blocking mode + * to get a pipeline of commands. */ +int redisvAppendCommand(redisContext *c, const char *format, va_list ap); +int redisAppendCommand(redisContext *c, const char *format, ...); +int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); + +/* Issue a command to Redis. In a blocking context, it is identical to calling + * redisAppendCommand, followed by redisGetReply. The function will return + * NULL if there was an error in performing the request, otherwise it will + * return the reply. In a non-blocking context, it is identical to calling + * only redisAppendCommand and will always return NULL. */ +void *redisvCommand(redisContext *c, const char *format, va_list ap); +void *redisCommand(redisContext *c, const char *format, ...); +void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/data-server/external/hiredis/net.c b/data-server/external/hiredis/net.c new file mode 100755 index 0000000000..603699c936 --- /dev/null +++ b/data-server/external/hiredis/net.c @@ -0,0 +1,339 @@ +/* Extracted from anet.c to work properly with Hiredis error reporting. + * + * Copyright (c) 2006-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "net.h" +#include "sds.h" + +/* Defined in hiredis.c */ +void __redisSetError(redisContext *c, int type, const char *str); + +static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) { + char buf[128] = { 0 }; + size_t len = 0; + + if (prefix != NULL) + len = snprintf(buf,sizeof(buf),"%s: ",prefix); + strerror_r(errno,buf+len,sizeof(buf)-len); + __redisSetError(c,type,buf); +} + +static int redisSetReuseAddr(redisContext *c, int fd) { + int on = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + close(fd); + return REDIS_ERR; + } + return REDIS_OK; +} + +static int redisCreateSocket(redisContext *c, int type) { + int s; + if ((s = socket(type, SOCK_STREAM, 0)) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + return REDIS_ERR; + } + if (type == AF_INET) { + if (redisSetReuseAddr(c,s) == REDIS_ERR) { + return REDIS_ERR; + } + } + return s; +} + +static int redisSetBlocking(redisContext *c, int fd, int blocking) { + int flags; + + /* Set the socket nonblocking. + * Note that fcntl(2) for F_GETFL and F_SETFL can't be + * interrupted by a signal. */ + if ((flags = fcntl(fd, F_GETFL)) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)"); + close(fd); + return REDIS_ERR; + } + + if (blocking) + flags &= ~O_NONBLOCK; + else + flags |= O_NONBLOCK; + + if (fcntl(fd, F_SETFL, flags) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)"); + close(fd); + return REDIS_ERR; + } + return REDIS_OK; +} + +int redisKeepAlive(redisContext *c, int interval) { + int val = 1; + int fd = c->fd; + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){ + __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); + return REDIS_ERR; + } + +#ifdef _OSX + val = interval; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &val, sizeof(val)) < 0) { + __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); + return REDIS_ERR; + } +#else + val = interval; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) { + __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); + return REDIS_ERR; + } + + val = interval/3; + if (val == 0) val = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) { + __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); + return REDIS_ERR; + } + + val = 3; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) { + __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); + return REDIS_ERR; + } +#endif + + return REDIS_OK; +} + +static int redisSetTcpNoDelay(redisContext *c, int fd) { + int yes = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)"); + close(fd); + return REDIS_ERR; + } + return REDIS_OK; +} + +#define __MAX_MSEC (((LONG_MAX) - 999) / 1000) + +static int redisContextWaitReady(redisContext *c, int fd, const struct timeval *timeout) { + struct pollfd wfd[1]; + long msec; + + msec = -1; + wfd[0].fd = fd; + wfd[0].events = POLLOUT; + + /* Only use timeout when not NULL. */ + if (timeout != NULL) { + if (timeout->tv_usec > 1000000 || timeout->tv_sec > __MAX_MSEC) { + __redisSetErrorFromErrno(c, REDIS_ERR_IO, NULL); + close(fd); + return REDIS_ERR; + } + + msec = (timeout->tv_sec * 1000) + ((timeout->tv_usec + 999) / 1000); + + if (msec < 0 || msec > INT_MAX) { + msec = INT_MAX; + } + } + + if (errno == EINPROGRESS) { + int res; + + if ((res = poll(wfd, 1, msec)) == -1) { + __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)"); + close(fd); + return REDIS_ERR; + } else if (res == 0) { + errno = ETIMEDOUT; + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + close(fd); + return REDIS_ERR; + } + + if (redisCheckSocketError(c, fd) != REDIS_OK) + return REDIS_ERR; + + return REDIS_OK; + } + + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + close(fd); + return REDIS_ERR; +} + +int redisCheckSocketError(redisContext *c, int fd) { + int err = 0; + socklen_t errlen = sizeof(err); + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"getsockopt(SO_ERROR)"); + close(fd); + return REDIS_ERR; + } + + if (err) { + errno = err; + __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); + close(fd); + return REDIS_ERR; + } + + return REDIS_OK; +} + +int redisContextSetTimeout(redisContext *c, const struct timeval tv) { + if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); + return REDIS_ERR; + } + if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)"); + return REDIS_ERR; + } + return REDIS_OK; +} + +int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout) { + int s, rv; + char _port[6]; /* strlen("65535"); */ + struct addrinfo hints, *servinfo, *p; + int blocking = (c->flags & REDIS_BLOCK); + + snprintf(_port, 6, "%d", port); + memset(&hints,0,sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + /* Try with IPv6 if no IPv4 address was found. We do it in this order since + * in a Redis client you can't afford to test if you have IPv6 connectivity + * as this would add latency to every connect. Otherwise a more sensible + * route could be: Use IPv6 if both addresses are available and there is IPv6 + * connectivity. */ + if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) { + hints.ai_family = AF_INET6; + if ((rv = getaddrinfo(addr,_port,&hints,&servinfo)) != 0) { + __redisSetError(c,REDIS_ERR_OTHER,gai_strerror(rv)); + return REDIS_ERR; + } + } + for (p = servinfo; p != NULL; p = p->ai_next) { + if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) + continue; + + if (redisSetBlocking(c,s,0) != REDIS_OK) + goto error; + if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { + if (errno == EHOSTUNREACH) { + close(s); + continue; + } else if (errno == EINPROGRESS && !blocking) { + /* This is ok. */ + } else { + if (redisContextWaitReady(c,s,timeout) != REDIS_OK) + goto error; + } + } + if (blocking && redisSetBlocking(c,s,1) != REDIS_OK) + goto error; + if (redisSetTcpNoDelay(c,s) != REDIS_OK) + goto error; + + c->fd = s; + c->flags |= REDIS_CONNECTED; + rv = REDIS_OK; + goto end; + } + if (p == NULL) { + char buf[128]; + snprintf(buf,sizeof(buf),"Can't create socket: %s",strerror(errno)); + __redisSetError(c,REDIS_ERR_OTHER,buf); + goto error; + } + +error: + rv = REDIS_ERR; +end: + freeaddrinfo(servinfo); + return rv; // Need to return REDIS_OK if alright +} + +int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) { + int s; + int blocking = (c->flags & REDIS_BLOCK); + struct sockaddr_un sa; + + if ((s = redisCreateSocket(c,AF_LOCAL)) < 0) + return REDIS_ERR; + if (redisSetBlocking(c,s,0) != REDIS_OK) + return REDIS_ERR; + + sa.sun_family = AF_LOCAL; + strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); + if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) { + if (errno == EINPROGRESS && !blocking) { + /* This is ok. */ + } else { + if (redisContextWaitReady(c,s,timeout) != REDIS_OK) + return REDIS_ERR; + } + } + + /* Reset socket to be blocking after connect(2). */ + if (blocking && redisSetBlocking(c,s,1) != REDIS_OK) + return REDIS_ERR; + + c->fd = s; + c->flags |= REDIS_CONNECTED; + return REDIS_OK; +} diff --git a/data-server/external/hiredis/net.h b/data-server/external/hiredis/net.h new file mode 100755 index 0000000000..94b76f5ca7 --- /dev/null +++ b/data-server/external/hiredis/net.h @@ -0,0 +1,48 @@ +/* Extracted from anet.c to work properly with Hiredis error reporting. + * + * Copyright (c) 2006-2011, Salvatore Sanfilippo + * Copyright (c) 2010-2011, Pieter Noordhuis + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __NET_H +#define __NET_H + +#include "hiredis.h" + +#if defined(__sun) +#define AF_LOCAL AF_UNIX +#endif + +int redisCheckSocketError(redisContext *c, int fd); +int redisContextSetTimeout(redisContext *c, const struct timeval tv); +int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); +int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout); +int redisKeepAlive(redisContext *c, int interval); + +#endif diff --git a/data-server/external/hiredis/sds.c b/data-server/external/hiredis/sds.c new file mode 100755 index 0000000000..9226799fcf --- /dev/null +++ b/data-server/external/hiredis/sds.c @@ -0,0 +1,606 @@ +/* SDSLib, A C dynamic strings library + * + * Copyright (c) 2006-2010, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include "sds.h" + +#ifdef SDS_ABORT_ON_OOM +static void sdsOomAbort(void) { + fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n"); + abort(); +} +#endif + +sds sdsnewlen(const void *init, size_t initlen) { + struct sdshdr *sh; + + sh = malloc(sizeof(struct sdshdr)+initlen+1); +#ifdef SDS_ABORT_ON_OOM + if (sh == NULL) sdsOomAbort(); +#else + if (sh == NULL) return NULL; +#endif + sh->len = initlen; + sh->free = 0; + if (initlen) { + if (init) memcpy(sh->buf, init, initlen); + else memset(sh->buf,0,initlen); + } + sh->buf[initlen] = '\0'; + return (char*)sh->buf; +} + +sds sdsempty(void) { + return sdsnewlen("",0); +} + +sds sdsnew(const char *init) { + size_t initlen = (init == NULL) ? 0 : strlen(init); + return sdsnewlen(init, initlen); +} + +sds sdsdup(const sds s) { + return sdsnewlen(s, sdslen(s)); +} + +void sdsfree(sds s) { + if (s == NULL) return; + free(s-sizeof(struct sdshdr)); +} + +void sdsupdatelen(sds s) { + struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); + int reallen = strlen(s); + sh->free += (sh->len-reallen); + sh->len = reallen; +} + +static sds sdsMakeRoomFor(sds s, size_t addlen) { + struct sdshdr *sh, *newsh; + size_t free = sdsavail(s); + size_t len, newlen; + + if (free >= addlen) return s; + len = sdslen(s); + sh = (void*) (s-(sizeof(struct sdshdr))); + newlen = (len+addlen)*2; + newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1); +#ifdef SDS_ABORT_ON_OOM + if (newsh == NULL) sdsOomAbort(); +#else + if (newsh == NULL) return NULL; +#endif + + newsh->free = newlen - len; + return newsh->buf; +} + +/* Grow the sds to have the specified length. Bytes that were not part of + * the original length of the sds will be set to zero. */ +sds sdsgrowzero(sds s, size_t len) { + struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); + size_t totlen, curlen = sh->len; + + if (len <= curlen) return s; + s = sdsMakeRoomFor(s,len-curlen); + if (s == NULL) return NULL; + + /* Make sure added region doesn't contain garbage */ + sh = (void*)(s-(sizeof(struct sdshdr))); + memset(s+curlen,0,(len-curlen+1)); /* also set trailing \0 byte */ + totlen = sh->len+sh->free; + sh->len = len; + sh->free = totlen-sh->len; + return s; +} + +sds sdscatlen(sds s, const void *t, size_t len) { + struct sdshdr *sh; + size_t curlen = sdslen(s); + + s = sdsMakeRoomFor(s,len); + if (s == NULL) return NULL; + sh = (void*) (s-(sizeof(struct sdshdr))); + memcpy(s+curlen, t, len); + sh->len = curlen+len; + sh->free = sh->free-len; + s[curlen+len] = '\0'; + return s; +} + +sds sdscat(sds s, const char *t) { + return sdscatlen(s, t, strlen(t)); +} + +sds sdscpylen(sds s, char *t, size_t len) { + struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); + size_t totlen = sh->free+sh->len; + + if (totlen < len) { + s = sdsMakeRoomFor(s,len-sh->len); + if (s == NULL) return NULL; + sh = (void*) (s-(sizeof(struct sdshdr))); + totlen = sh->free+sh->len; + } + memcpy(s, t, len); + s[len] = '\0'; + sh->len = len; + sh->free = totlen-len; + return s; +} + +sds sdscpy(sds s, char *t) { + return sdscpylen(s, t, strlen(t)); +} + +sds sdscatvprintf(sds s, const char *fmt, va_list ap) { + va_list cpy; + char *buf, *t; + size_t buflen = 16; + + while(1) { + buf = malloc(buflen); +#ifdef SDS_ABORT_ON_OOM + if (buf == NULL) sdsOomAbort(); +#else + if (buf == NULL) return NULL; +#endif + buf[buflen-2] = '\0'; + va_copy(cpy,ap); + vsnprintf(buf, buflen, fmt, cpy); + va_end(cpy); + if (buf[buflen-2] != '\0') { + free(buf); + buflen *= 2; + continue; + } + break; + } + t = sdscat(s, buf); + free(buf); + return t; +} + +sds sdscatprintf(sds s, const char *fmt, ...) { + va_list ap; + char *t; + va_start(ap, fmt); + t = sdscatvprintf(s,fmt,ap); + va_end(ap); + return t; +} + +sds sdstrim(sds s, const char *cset) { + struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); + char *start, *end, *sp, *ep; + size_t len; + + sp = start = s; + ep = end = s+sdslen(s)-1; + while(sp <= end && strchr(cset, *sp)) sp++; + while(ep > start && strchr(cset, *ep)) ep--; + len = (sp > ep) ? 0 : ((ep-sp)+1); + if (sh->buf != sp) memmove(sh->buf, sp, len); + sh->buf[len] = '\0'; + sh->free = sh->free+(sh->len-len); + sh->len = len; + return s; +} + +sds sdsrange(sds s, int start, int end) { + struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); + size_t newlen, len = sdslen(s); + + if (len == 0) return s; + if (start < 0) { + start = len+start; + if (start < 0) start = 0; + } + if (end < 0) { + end = len+end; + if (end < 0) end = 0; + } + newlen = (start > end) ? 0 : (end-start)+1; + if (newlen != 0) { + if (start >= (signed)len) { + newlen = 0; + } else if (end >= (signed)len) { + end = len-1; + newlen = (start > end) ? 0 : (end-start)+1; + } + } else { + start = 0; + } + if (start && newlen) memmove(sh->buf, sh->buf+start, newlen); + sh->buf[newlen] = 0; + sh->free = sh->free+(sh->len-newlen); + sh->len = newlen; + return s; +} + +void sdstolower(sds s) { + int len = sdslen(s), j; + + for (j = 0; j < len; j++) s[j] = tolower(s[j]); +} + +void sdstoupper(sds s) { + int len = sdslen(s), j; + + for (j = 0; j < len; j++) s[j] = toupper(s[j]); +} + +int sdscmp(sds s1, sds s2) { + size_t l1, l2, minlen; + int cmp; + + l1 = sdslen(s1); + l2 = sdslen(s2); + minlen = (l1 < l2) ? l1 : l2; + cmp = memcmp(s1,s2,minlen); + if (cmp == 0) return l1-l2; + return cmp; +} + +/* Split 's' with separator in 'sep'. An array + * of sds strings is returned. *count will be set + * by reference to the number of tokens returned. + * + * On out of memory, zero length string, zero length + * separator, NULL is returned. + * + * Note that 'sep' is able to split a string using + * a multi-character separator. For example + * sdssplit("foo_-_bar","_-_"); will return two + * elements "foo" and "bar". + * + * This version of the function is binary-safe but + * requires length arguments. sdssplit() is just the + * same function but for zero-terminated strings. + */ +sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) { + int elements = 0, slots = 5, start = 0, j; + + sds *tokens = malloc(sizeof(sds)*slots); +#ifdef SDS_ABORT_ON_OOM + if (tokens == NULL) sdsOomAbort(); +#endif + if (seplen < 1 || len < 0 || tokens == NULL) return NULL; + if (len == 0) { + *count = 0; + return tokens; + } + for (j = 0; j < (len-(seplen-1)); j++) { + /* make sure there is room for the next element and the final one */ + if (slots < elements+2) { + sds *newtokens; + + slots *= 2; + newtokens = realloc(tokens,sizeof(sds)*slots); + if (newtokens == NULL) { +#ifdef SDS_ABORT_ON_OOM + sdsOomAbort(); +#else + goto cleanup; +#endif + } + tokens = newtokens; + } + /* search the separator */ + if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) { + tokens[elements] = sdsnewlen(s+start,j-start); + if (tokens[elements] == NULL) { +#ifdef SDS_ABORT_ON_OOM + sdsOomAbort(); +#else + goto cleanup; +#endif + } + elements++; + start = j+seplen; + j = j+seplen-1; /* skip the separator */ + } + } + /* Add the final element. We are sure there is room in the tokens array. */ + tokens[elements] = sdsnewlen(s+start,len-start); + if (tokens[elements] == NULL) { +#ifdef SDS_ABORT_ON_OOM + sdsOomAbort(); +#else + goto cleanup; +#endif + } + elements++; + *count = elements; + return tokens; + +#ifndef SDS_ABORT_ON_OOM +cleanup: + { + int i; + for (i = 0; i < elements; i++) sdsfree(tokens[i]); + free(tokens); + return NULL; + } +#endif +} + +void sdsfreesplitres(sds *tokens, int count) { + if (!tokens) return; + while(count--) + sdsfree(tokens[count]); + free(tokens); +} + +sds sdsfromlonglong(long long value) { + char buf[32], *p; + unsigned long long v; + + v = (value < 0) ? -value : value; + p = buf+31; /* point to the last character */ + do { + *p-- = '0'+(v%10); + v /= 10; + } while(v); + if (value < 0) *p-- = '-'; + p++; + return sdsnewlen(p,32-(p-buf)); +} + +sds sdscatrepr(sds s, char *p, size_t len) { + s = sdscatlen(s,"\"",1); + if (s == NULL) return NULL; + + while(len--) { + switch(*p) { + case '\\': + case '"': + s = sdscatprintf(s,"\\%c",*p); + break; + case '\n': s = sdscatlen(s,"\\n",2); break; + case '\r': s = sdscatlen(s,"\\r",2); break; + case '\t': s = sdscatlen(s,"\\t",2); break; + case '\a': s = sdscatlen(s,"\\a",2); break; + case '\b': s = sdscatlen(s,"\\b",2); break; + default: + if (isprint(*p)) + s = sdscatprintf(s,"%c",*p); + else + s = sdscatprintf(s,"\\x%02x",(unsigned char)*p); + break; + } + p++; + if (s == NULL) return NULL; + } + return sdscatlen(s,"\"",1); +} + +/* Split a line into arguments, where every argument can be in the + * following programming-language REPL-alike form: + * + * foo bar "newline are supported\n" and "\xff\x00otherstuff" + * + * The number of arguments is stored into *argc, and an array + * of sds is returned. The caller should sdsfree() all the returned + * strings and finally free() the array itself. + * + * Note that sdscatrepr() is able to convert back a string into + * a quoted string in the same format sdssplitargs() is able to parse. + */ +sds *sdssplitargs(char *line, int *argc) { + char *p = line; + char *current = NULL; + char **vector = NULL, **_vector = NULL; + + *argc = 0; + while(1) { + /* skip blanks */ + while(*p && isspace(*p)) p++; + if (*p) { + /* get a token */ + int inq=0; /* set to 1 if we are in "quotes" */ + int done=0; + + if (current == NULL) { + current = sdsempty(); + if (current == NULL) goto err; + } + + while(!done) { + if (inq) { + if (*p == '\\' && *(p+1)) { + char c; + + p++; + switch(*p) { + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'b': c = '\b'; break; + case 'a': c = '\a'; break; + default: c = *p; break; + } + current = sdscatlen(current,&c,1); + } else if (*p == '"') { + /* closing quote must be followed by a space */ + if (*(p+1) && !isspace(*(p+1))) goto err; + done=1; + } else if (!*p) { + /* unterminated quotes */ + goto err; + } else { + current = sdscatlen(current,p,1); + } + } else { + switch(*p) { + case ' ': + case '\n': + case '\r': + case '\t': + case '\0': + done=1; + break; + case '"': + inq=1; + break; + default: + current = sdscatlen(current,p,1); + break; + } + } + if (*p) p++; + if (current == NULL) goto err; + } + /* add the token to the vector */ + _vector = realloc(vector,((*argc)+1)*sizeof(char*)); + if (_vector == NULL) goto err; + + vector = _vector; + vector[*argc] = current; + (*argc)++; + current = NULL; + } else { + return vector; + } + } + +err: + while((*argc)--) + sdsfree(vector[*argc]); + if (vector != NULL) free(vector); + if (current != NULL) sdsfree(current); + return NULL; +} + +#ifdef SDS_TEST_MAIN +#include + +int __failed_tests = 0; +int __test_num = 0; +#define test_cond(descr,_c) do { \ + __test_num++; printf("%d - %s: ", __test_num, descr); \ + if(_c) printf("PASSED\n"); else {printf("FAILED\n"); __failed_tests++;} \ +} while(0); +#define test_report() do { \ + printf("%d tests, %d passed, %d failed\n", __test_num, \ + __test_num-__failed_tests, __failed_tests); \ + if (__failed_tests) { \ + printf("=== WARNING === We have failed tests here...\n"); \ + } \ +} while(0); + +int main(void) { + { + sds x = sdsnew("foo"), y; + + test_cond("Create a string and obtain the length", + sdslen(x) == 3 && memcmp(x,"foo\0",4) == 0) + + sdsfree(x); + x = sdsnewlen("foo",2); + test_cond("Create a string with specified length", + sdslen(x) == 2 && memcmp(x,"fo\0",3) == 0) + + x = sdscat(x,"bar"); + test_cond("Strings concatenation", + sdslen(x) == 5 && memcmp(x,"fobar\0",6) == 0); + + x = sdscpy(x,"a"); + test_cond("sdscpy() against an originally longer string", + sdslen(x) == 1 && memcmp(x,"a\0",2) == 0) + + x = sdscpy(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk"); + test_cond("sdscpy() against an originally shorter string", + sdslen(x) == 33 && + memcmp(x,"xyzxxxxxxxxxxyyyyyyyyyykkkkkkkkkk\0",33) == 0) + + sdsfree(x); + x = sdscatprintf(sdsempty(),"%d",123); + test_cond("sdscatprintf() seems working in the base case", + sdslen(x) == 3 && memcmp(x,"123\0",4) ==0) + + sdsfree(x); + x = sdstrim(sdsnew("xxciaoyyy"),"xy"); + test_cond("sdstrim() correctly trims characters", + sdslen(x) == 4 && memcmp(x,"ciao\0",5) == 0) + + y = sdsrange(sdsdup(x),1,1); + test_cond("sdsrange(...,1,1)", + sdslen(y) == 1 && memcmp(y,"i\0",2) == 0) + + sdsfree(y); + y = sdsrange(sdsdup(x),1,-1); + test_cond("sdsrange(...,1,-1)", + sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) + + sdsfree(y); + y = sdsrange(sdsdup(x),-2,-1); + test_cond("sdsrange(...,-2,-1)", + sdslen(y) == 2 && memcmp(y,"ao\0",3) == 0) + + sdsfree(y); + y = sdsrange(sdsdup(x),2,1); + test_cond("sdsrange(...,2,1)", + sdslen(y) == 0 && memcmp(y,"\0",1) == 0) + + sdsfree(y); + y = sdsrange(sdsdup(x),1,100); + test_cond("sdsrange(...,1,100)", + sdslen(y) == 3 && memcmp(y,"iao\0",4) == 0) + + sdsfree(y); + y = sdsrange(sdsdup(x),100,100); + test_cond("sdsrange(...,100,100)", + sdslen(y) == 0 && memcmp(y,"\0",1) == 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("foo"); + y = sdsnew("foa"); + test_cond("sdscmp(foo,foa)", sdscmp(x,y) > 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("bar"); + y = sdsnew("bar"); + test_cond("sdscmp(bar,bar)", sdscmp(x,y) == 0) + + sdsfree(y); + sdsfree(x); + x = sdsnew("aar"); + y = sdsnew("bar"); + test_cond("sdscmp(bar,bar)", sdscmp(x,y) < 0) + } + test_report() +} +#endif diff --git a/data-server/external/hiredis/sds.h b/data-server/external/hiredis/sds.h new file mode 100755 index 0000000000..94f5871f54 --- /dev/null +++ b/data-server/external/hiredis/sds.h @@ -0,0 +1,88 @@ +/* SDSLib, A C dynamic strings library + * + * Copyright (c) 2006-2010, Salvatore Sanfilippo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SDS_H +#define __SDS_H + +#include +#include + +typedef char *sds; + +struct sdshdr { + int len; + int free; + char buf[]; +}; + +static inline size_t sdslen(const sds s) { + struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); + return sh->len; +} + +static inline size_t sdsavail(const sds s) { + struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); + return sh->free; +} + +sds sdsnewlen(const void *init, size_t initlen); +sds sdsnew(const char *init); +sds sdsempty(void); +size_t sdslen(const sds s); +sds sdsdup(const sds s); +void sdsfree(sds s); +size_t sdsavail(sds s); +sds sdsgrowzero(sds s, size_t len); +sds sdscatlen(sds s, const void *t, size_t len); +sds sdscat(sds s, const char *t); +sds sdscpylen(sds s, char *t, size_t len); +sds sdscpy(sds s, char *t); + +sds sdscatvprintf(sds s, const char *fmt, va_list ap); +#ifdef __GNUC__ +sds sdscatprintf(sds s, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); +#else +sds sdscatprintf(sds s, const char *fmt, ...); +#endif + +sds sdstrim(sds s, const char *cset); +sds sdsrange(sds s, int start, int end); +void sdsupdatelen(sds s); +int sdscmp(sds s1, sds s2); +sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count); +void sdsfreesplitres(sds *tokens, int count); +void sdstolower(sds s); +void sdstoupper(sds s); +sds sdsfromlonglong(long long value); +sds sdscatrepr(sds s, char *p, size_t len); +sds *sdssplitargs(char *line, int *argc); + +#endif diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp new file mode 100644 index 0000000000..7dbfdd2c07 --- /dev/null +++ b/data-server/src/DataServer.cpp @@ -0,0 +1,194 @@ +// +// DataServer.cpp +// hifi +// +// Created by Stephen Birarda on 1/20/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include + +#include +#include + +#include "DataServer.h" + +const quint16 DATA_SERVER_LISTEN_PORT = 3282; + +const char REDIS_HOSTNAME[] = "127.0.0.1"; +const unsigned short REDIS_PORT = 6379; + +DataServer::DataServer(int argc, char* argv[]) : + QCoreApplication(argc, argv), + _socket(), + _redis(NULL) +{ + _socket.bind(QHostAddress::Any, DATA_SERVER_LISTEN_PORT); + + connect(&_socket, &QUdpSocket::readyRead, this, &DataServer::readPendingDatagrams); + + _redis = redisConnect(REDIS_HOSTNAME, REDIS_PORT); + + if (_redis && _redis->err) { + if (_redis) { + qDebug() << "Redis connection error:" << _redis->errstr; + } else { + qDebug("Redis connection error - can't allocate redis context."); + } + + // couldn't connect to redis, bail out + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + } +} + +DataServer::~DataServer() { + // disconnect from redis and free the context + if (_redis) { + redisFree(_redis); + } +} + +const int MAX_PACKET_SIZE = 1500; + +void DataServer::readPendingDatagrams() { + qint64 receivedBytes = 0; + static unsigned char packetData[MAX_PACKET_SIZE]; + + QHostAddress senderAddress; + quint16 senderPort; + + while (_socket.hasPendingDatagrams() && + (receivedBytes = _socket.readDatagram(reinterpret_cast(packetData), MAX_PACKET_SIZE, + &senderAddress, &senderPort))) { + if ((packetData[0] == PACKET_TYPE_DATA_SERVER_PUT || packetData[0] == PACKET_TYPE_DATA_SERVER_GET) && + packetVersionMatch(packetData)) { + + int readBytes = numBytesForPacketHeader(packetData); + + // pull the UUID that we will need as part of the key + QString uuidString(reinterpret_cast(packetData + readBytes)); + QUuid parsedUUID(uuidString); + + if (parsedUUID.isNull()) { + // we failed to parse a UUID, this means the user has sent us a username + + QString username(reinterpret_cast(packetData + readBytes)); + readBytes += sizeof(username) + sizeof('\0'); + + + // ask redis for the UUID for this user + redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(username)); + + if (reply->type == REDIS_REPLY_STRING) { + + parsedUUID = QUuid(QString(reply->str)); + } + + if (!parsedUUID.isNull()) { + qDebug() << "Found UUID" << parsedUUID << "for username" << username; + } else { + qDebug() << "Failed UUID lookup for username" << username; + } + + freeReplyObject(reply); + reply = NULL; + } else { + readBytes += uuidString.size() + sizeof('\0'); + } + + if (!parsedUUID.isNull()) { + // pull the number of keys the user has sent + unsigned char numKeys = packetData[readBytes++]; + + if (packetData[0] == PACKET_TYPE_DATA_SERVER_PUT) { + + // pull the key that specifies the data the user is putting/getting + QString dataKey(reinterpret_cast(packetData + readBytes)); + readBytes += dataKey.size() + sizeof('\0'); + + // grab the string value the user wants us to put, null terminate it + QString dataValue(reinterpret_cast(packetData + readBytes)); + readBytes += dataValue.size() + sizeof('\0'); + + qDebug("Sending command to redis: SET uuid:%s:%s %s\n", + qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), + qPrintable(dataKey), qPrintable(dataValue)); + + redisReply* reply = (redisReply*) redisCommand(_redis, "SET uuid:%s:%s %s", + qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), + qPrintable(dataKey), qPrintable(dataValue)); + + if (reply->type == REDIS_REPLY_STATUS && strcmp("OK", reply->str) == 0) { + // if redis stored the value successfully reply back with a confirm + + // which is the sent packet with the header replaced + packetData[0] = PACKET_TYPE_DATA_SERVER_CONFIRM; + _socket.writeDatagram(reinterpret_cast(packetData), receivedBytes, + senderAddress, senderPort); + } + + freeReplyObject(reply); + } else { + // setup a send packet with the returned data + // leverage the packetData sent by overwriting and appending + int numSendPacketBytes = 0; + + numSendPacketBytes += populateTypeAndVersion(packetData, PACKET_TYPE_DATA_SERVER_SEND); + + const char MULTI_KEY_VALUE_SEPARATOR = '|'; + + if (strcmp((char*) packetData + readBytes, "uuid") != 0) { + + // the user has sent one or more keys - make the associated requests + + for (int j = 0; j < numKeys; j++) { + + // pull the key that specifies the data the user is putting/getting, null terminate it + int numDataKeyBytes = 0; + + // look for the key separator or the null terminator + while (packetData[readBytes + numDataKeyBytes] != MULTI_KEY_VALUE_SEPARATOR + && packetData[readBytes + numDataKeyBytes] != '\0') { + numDataKeyBytes++; + } + + QString dataKey(QByteArray(reinterpret_cast(packetData + readBytes), numDataKeyBytes)); + readBytes += dataKey.size() + sizeof('\0'); + + qDebug("Sending command to redis: GET uuid:%s:%s\n", + qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), + qPrintable(dataKey)); + redisReply* reply = (redisReply*) redisCommand(_redis, "GET uuid:%s:%s", + qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), + qPrintable(dataKey)); + + if (reply->len) { + // copy the value that redis returned + memcpy(packetData + numSendPacketBytes, reply->str, reply->len); + numSendPacketBytes += reply->len; + + } else { + // didn't find a value - insert a space + packetData[numSendPacketBytes++] = ' '; + } + + // add the multi-value separator + packetData[numSendPacketBytes++] = MULTI_KEY_VALUE_SEPARATOR; + + freeReplyObject(reply); + } + } else { + // user is asking for a UUID matching username, copy the UUID we found + QString uuidString = uuidStringWithoutCurlyBraces(parsedUUID); + memcpy(packetData + numSendPacketBytes, uuidString.constData(), uuidString.size() + sizeof('\0')); + numSendPacketBytes = uuidString.size() + sizeof('\0'); + } + + // reply back with the send packet + _socket.writeDatagram(reinterpret_cast(packetData), numSendPacketBytes, + senderAddress, senderPort); + } + } + } + } +} \ No newline at end of file diff --git a/data-server/src/DataServer.h b/data-server/src/DataServer.h new file mode 100644 index 0000000000..097f6a6533 --- /dev/null +++ b/data-server/src/DataServer.h @@ -0,0 +1,29 @@ +// +// DataServer.h +// hifi +// +// Created by Stephen Birarda on 1/20/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__DataServer__ +#define __hifi__DataServer__ + +#include +#include + +#include + +class DataServer : public QCoreApplication { + Q_OBJECT +public: + DataServer(int argc, char* argv[]); + ~DataServer(); +private: + QUdpSocket _socket; + redisContext* _redis; +private slots: + void readPendingDatagrams(); +}; + +#endif /* defined(__hifi__DataServer__) */ diff --git a/data-server/src/main.cpp b/data-server/src/main.cpp new file mode 100755 index 0000000000..8e71789032 --- /dev/null +++ b/data-server/src/main.cpp @@ -0,0 +1,24 @@ +// +// main.cpp +// data-server +// +// Created by Stephen Birarda on 1/1/13. +// Copyright (c) 2013 HighFidelity, Inc. All rights reserved. +// + +#include + +#include + +#include "DataServer.h" + +int main(int argc, char* argv[]) { + + setvbuf(stdout, NULL, _IOLBF, 0); + + qInstallMessageHandler(Logging::verboseMessageHandler); + + DataServer dataServer(argc, argv); + + return dataServer.exec(); +} \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 229cedf55c..4b8343e72f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1446,13 +1446,9 @@ void Application::processAvatarURLsMessage(unsigned char* packetData, size_t dat // Tell the other avatars that your dataserver data has // changed. - //QDataStream in(QByteArray((char*)packetData, dataBytes)); - //QUrl voxelURL; - //in >> voxelURL; - // use this timing to as the data-server for an updated mesh for this avatar (if we have UUID) DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL, - avatar->getUUID()); + avatar->getUUID(), &_profile); } void Application::checkBandwidthMeterClick() { @@ -1858,8 +1854,8 @@ void Application::init() { if (!_profile.getUsername().isEmpty()) { // we have a username for this avatar, ask the data-server for the mesh URL for this avatar - DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL); - DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL); + DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL, &_profile); + DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL, &_profile); } // Set up VoxelSystem after loading preferences so we can get the desired max voxel count @@ -3960,12 +3956,14 @@ void Application::attachNewHeadToNode(Node* newNode) { void Application::updateWindowTitle(){ QString title = ""; QString buildVersion = " (build " + QString::number(BUILD_VERSION) + ")"; + QString username = _profile.getUsername(); if(!username.isEmpty()){ - title += _profile.getUsername(); + title += username; title += " @ "; } - title += _profile.getLastDomain(); + + title += NodeList::getInstance()->getDomainHostname(); title += buildVersion; qDebug("Application title set to: %s", title.toStdString().c_str()); diff --git a/interface/src/Application.h b/interface/src/Application.h index 7e78cced4c..45edc25b8e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -256,7 +256,7 @@ private: void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true); static bool sendVoxelsOperation(OctreeElement* node, void* extraData); - static void processAvatarURLsMessage(unsigned char* packetData, size_t dataBytes); + void processAvatarURLsMessage(unsigned char* packetData, size_t dataBytes); static void sendPingPackets(); void initDisplay(); diff --git a/interface/src/DataServerClient.cpp b/interface/src/DataServerClient.cpp index 45bdd6f6a9..af76d8eea2 100644 --- a/interface/src/DataServerClient.cpp +++ b/interface/src/DataServerClient.cpp @@ -13,36 +13,40 @@ #include #include -#include "Application.h" #include "avatar/Profile.h" #include "DataServerClient.h" -std::map DataServerClient::_unmatchedPackets; +QMap DataServerClient::_unmatchedPackets; +QMap DataServerClient::_callbackObjects; +QString DataServerClient::_clientIdentifier; +quint8 DataServerClient::_sequenceNumber = 0; const char MULTI_KEY_VALUE_SEPARATOR = '|'; const char DATA_SERVER_HOSTNAME[] = "data.highfidelity.io"; const unsigned short DATA_SERVER_PORT = 3282; - const HifiSockAddr& DataServerClient::dataServerSockAddr() { static HifiSockAddr dsSockAddr = HifiSockAddr(DATA_SERVER_HOSTNAME, DATA_SERVER_PORT); return dsSockAddr; } void DataServerClient::putValueForKey(const QString& key, const char* value) { - QString clientString = Application::getInstance()->getProfile()->getUserString(); - if (!clientString.isEmpty()) { - - unsigned char* putPacket = new unsigned char[MAX_PACKET_SIZE]; - + if (!_clientIdentifier.isEmpty()) { + + static unsigned char putPacket[MAX_PACKET_SIZE]; + // setup the header for this packet int numPacketBytes = populateTypeAndVersion(putPacket, PACKET_TYPE_DATA_SERVER_PUT); - + + // pack the sequence number + memcpy(putPacket + numPacketBytes, &_sequenceNumber, sizeof(_sequenceNumber)); + numPacketBytes += sizeof(_sequenceNumber); + // pack the client UUID, null terminated - memcpy(putPacket + numPacketBytes, clientString.toLocal8Bit().constData(), clientString.toLocal8Bit().size()); - numPacketBytes += clientString.toLocal8Bit().size(); + memcpy(putPacket + numPacketBytes, qPrintable(_clientIdentifier), _clientIdentifier.toLocal8Bit().size()); + numPacketBytes += _clientIdentifier.toLocal8Bit().size(); putPacket[numPacketBytes++] = '\0'; // pack a 1 to designate that we are putting a single value @@ -59,31 +63,39 @@ void DataServerClient::putValueForKey(const QString& key, const char* value) { putPacket[numPacketBytes++] = '\0'; // add the putPacket to our vector of unconfirmed packets, will be deleted once put is confirmed - // _unmatchedPackets.insert(std::pair(putPacket, numPacketBytes)); - + _unmatchedPackets.insert(_sequenceNumber, QByteArray((char*) putPacket, numPacketBytes)); + // send this put request to the data server NodeList::getInstance()->getNodeSocket().writeDatagram((char*) putPacket, numPacketBytes, dataServerSockAddr().getAddress(), dataServerSockAddr().getPort()); + + // push the sequence number forwards + _sequenceNumber++; } } -void DataServerClient::getValueForKeyAndUUID(const QString& key, const QUuid &uuid) { - getValuesForKeysAndUUID(QStringList(key), uuid); +void DataServerClient::getValueForKeyAndUUID(const QString& key, const QUuid &uuid, DataServerCallbackObject* callbackObject) { + getValuesForKeysAndUUID(QStringList(key), uuid, callbackObject); } -void DataServerClient::getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid) { +void DataServerClient::getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, DataServerCallbackObject* callbackObject) { if (!uuid.isNull()) { - getValuesForKeysAndUserString(keys, uuidStringWithoutCurlyBraces(uuid)); + getValuesForKeysAndUserString(keys, uuidStringWithoutCurlyBraces(uuid), callbackObject); } } -void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, const QString& userString) { +void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, const QString& userString, + DataServerCallbackObject* callbackObject) { if (!userString.isEmpty() && keys.size() <= UCHAR_MAX) { - unsigned char* getPacket = new unsigned char[MAX_PACKET_SIZE]; - + static unsigned char getPacket[MAX_PACKET_SIZE]; + // setup the header for this packet int numPacketBytes = populateTypeAndVersion(getPacket, PACKET_TYPE_DATA_SERVER_GET); + + // pack the sequence number + memcpy(getPacket + numPacketBytes, &_sequenceNumber, sizeof(_sequenceNumber)); + numPacketBytes += sizeof(_sequenceNumber); // pack the user string (could be username or UUID string), null-terminate memcpy(getPacket + numPacketBytes, userString.toLocal8Bit().constData(), userString.toLocal8Bit().size()); @@ -99,121 +111,133 @@ void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, co strcpy((char*) getPacket + numPacketBytes, keyString.toLocal8Bit().constData()); numPacketBytes += keyString.size() + sizeof('\0'); - // add the getPacket to our vector of uncofirmed packets, will be deleted once we get a response from the nameserver - // _unmatchedPackets.insert(std::pair(getPacket, numPacketBytes)); - + // add the getPacket to our map of unconfirmed packets, will be deleted once we get a response from the nameserver + _unmatchedPackets.insert(_sequenceNumber, QByteArray((char*) getPacket, numPacketBytes)); + _callbackObjects.insert(_sequenceNumber, callbackObject); + // send the get to the data server NodeList::getInstance()->getNodeSocket().writeDatagram((char*) getPacket, numPacketBytes, dataServerSockAddr().getAddress(), dataServerSockAddr().getPort()); + + _sequenceNumber++; } } -void DataServerClient::getClientValueForKey(const QString& key) { - getValuesForKeysAndUserString(QStringList(key), Application::getInstance()->getProfile()->getUserString()); +void DataServerClient::getClientValueForKey(const QString& key, DataServerCallbackObject* callbackObject) { + if (!_clientIdentifier.isEmpty()) { + getValuesForKeysAndUserString(QStringList(key), _clientIdentifier, callbackObject); + } else { + qDebug() << "There is no client identifier set for the DataServerClient."; + } } -void DataServerClient::processConfirmFromDataServer(unsigned char* packetData, int numPacketBytes) { - removeMatchedPacketFromMap(packetData, numPacketBytes); +void DataServerClient::processConfirmFromDataServer(unsigned char* packetData) { + removeMatchedPacketFromMap(packetData); } void DataServerClient::processSendFromDataServer(unsigned char* packetData, int numPacketBytes) { // pull the user string from the packet so we know who to associate this with int numHeaderBytes = numBytesForPacketHeader(packetData); - - char* userStringPosition = (char*) packetData + numHeaderBytes; - - QString userString(QByteArray(userStringPosition, strlen(userStringPosition))); - - QUuid userUUID(userString); - - char* keysPosition = (char*) packetData + numHeaderBytes + strlen(userStringPosition) + + _sequenceNumber = *(packetData + numHeaderBytes); + + if (_callbackObjects.find(_sequenceNumber) != _callbackObjects.end()) { + DataServerCallbackObject* callbackObject = _callbackObjects.take(_sequenceNumber); + + char* userStringPosition = (char*) packetData + numHeaderBytes + sizeof(_sequenceNumber); + + QString userString(QByteArray(userStringPosition, strlen(userStringPosition))); + + QUuid userUUID(userString); + + char* keysPosition = (char*) packetData + numHeaderBytes + strlen(userStringPosition) + sizeof('\0') + sizeof(unsigned char); - char* valuesPosition = keysPosition + strlen(keysPosition) + sizeof('\0'); - - QStringList keyList = QString(keysPosition).split(MULTI_KEY_VALUE_SEPARATOR); - QStringList valueList = QString(valuesPosition).split(MULTI_KEY_VALUE_SEPARATOR); - - // user string was UUID, find matching avatar and associate data - for (int i = 0; i < keyList.size(); i++) { - if (valueList[i] != " ") { - if (keyList[i] == DataServerKey::FaceMeshURL) { - - if (userUUID.isNull() || userUUID == Application::getInstance()->getProfile()->getUUID()) { - qDebug("Changing user's face model URL to %s", valueList[i].toLocal8Bit().constData()); - Application::getInstance()->getProfile()->setFaceModelURL(QUrl(valueList[i])); - } else { - // mesh URL for a UUID, find avatar in our list - - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { - Avatar* avatar = (Avatar *) node->getLinkedData(); - - if (avatar->getUUID() == userUUID) { - QMetaObject::invokeMethod(&avatar->getHead().getFaceModel(), - "setURL", Q_ARG(QUrl, QUrl(valueList[i]))); - } - } - } - } - } else if (keyList[i] == DataServerKey::SkeletonURL) { - - if (userUUID.isNull() || userUUID == Application::getInstance()->getProfile()->getUUID()) { - qDebug("Changing user's skeleton URL to %s", valueList[i].toLocal8Bit().constData()); - Application::getInstance()->getProfile()->setSkeletonModelURL(QUrl(valueList[i])); - } else { - // skeleton URL for a UUID, find avatar in our list - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { - Avatar* avatar = (Avatar *) node->getLinkedData(); - - if (avatar->getUUID() == userUUID) { - QMetaObject::invokeMethod(&avatar->getSkeletonModel(), "setURL", - Q_ARG(QUrl, QUrl(valueList[i]))); - } - } - } - } - } else if (keyList[i] == DataServerKey::Domain && keyList[i + 1] == DataServerKey::Position && - keyList[i + 2] == DataServerKey::Orientation && valueList[i] != " " && - valueList[i + 1] != " " && valueList[i + 2] != " ") { - - QStringList coordinateItems = valueList[i + 1].split(','); - QStringList orientationItems = valueList[i + 2].split(','); - - if (coordinateItems.size() == 3 && orientationItems.size() == 3) { - - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); - - qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() << - ", position to" << valueList[i + 1].toLocal8Bit().constData() << - ", and orientation to" << valueList[i + 2].toLocal8Bit().constData() << - "to go to" << userString; - - NodeList::getInstance()->setDomainHostname(valueList[i]); - // orient the user to face the target - glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(), - orientationItems[1].toFloat(), orientationItems[2].toFloat()))) * - glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f); - Application::getInstance()->getAvatar()->setOrientation(newOrientation); - - // move the user a couple units away - const float DISTANCE_TO_USER = 2.0f; - glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(), - coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; - Application::getInstance()->getAvatar()->setPosition(newPosition); - } - - } else if (keyList[i] == DataServerKey::UUID) { - // this is the user's UUID - set it on the profile - Application::getInstance()->getProfile()->setUUID(valueList[i]); - } - } + char* valuesPosition = keysPosition + strlen(keysPosition) + sizeof('\0'); + + QStringList keyList = QString(keysPosition).split(MULTI_KEY_VALUE_SEPARATOR); + QStringList valueList = QString(valuesPosition).split(MULTI_KEY_VALUE_SEPARATOR); + + callbackObject->processDataServerResponse(userUUID, keyList, valueList); } - // remove the matched packet from our map so it isn't re-sent to the data-server - // removeMatchedPacketFromMap(packetData, numPacketBytes); + + // user string was UUID, find matching avatar and associate data +// for (int i = 0; i < keyList.size(); i++) { +// if (valueList[i] != " ") { +// if (keyList[i] == DataServerKey::FaceMeshURL) { + +// if (userUUID.isNull() || userUUID == Application::getInstance()->getProfile()->getUUID()) { +// qDebug("Changing user's face model URL to %s\n", valueList[i].toLocal8Bit().constData()); +// Application::getInstance()->getProfile()->setFaceModelURL(QUrl(valueList[i])); +// } else { +// // mesh URL for a UUID, find avatar in our list +// +// foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { +// if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { +// Avatar* avatar = (Avatar *) node->getLinkedData(); +// +// if (avatar->getUUID() == userUUID) { +// QMetaObject::invokeMethod(&avatar->getHead().getFaceModel(), +// "setURL", Q_ARG(QUrl, QUrl(valueList[i]))); +// } +// } +// } +// } +// } else if (keyList[i] == DataServerKey::SkeletonURL) { + +// if (userUUID.isNull() || userUUID == Application::getInstance()->getProfile()->getUUID()) { +// qDebug("Changing user's skeleton URL to %s\n", valueList[i].toLocal8Bit().constData()); +// Application::getInstance()->getProfile()->setSkeletonModelURL(QUrl(valueList[i])); +// } else { +// // skeleton URL for a UUID, find avatar in our list +// foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { +// if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { +// Avatar* avatar = (Avatar *) node->getLinkedData(); +// +// if (avatar->getUUID() == userUUID) { +// QMetaObject::invokeMethod(&avatar->getSkeletonModel(), "setURL", +// Q_ARG(QUrl, QUrl(valueList[i]))); +// } +// } +// } +// } +// } else if (keyList[i] == DataServerKey::Domain && keyList[i + 1] == DataServerKey::Position && +// keyList[i + 2] == DataServerKey::Orientation && valueList[i] != " " && +// valueList[i + 1] != " " && valueList[i + 2] != " ") { +// +// QStringList coordinateItems = valueList[i + 1].split(','); +// QStringList orientationItems = valueList[i + 2].split(','); + +// if (coordinateItems.size() == 3 && orientationItems.size() == 3) { +// +// // send a node kill request, indicating to other clients that they should play the "disappeared" effect +// NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); +// +// qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() << +// ", position to" << valueList[i + 1].toLocal8Bit().constData() << +// ", and orientation to" << valueList[i + 2].toLocal8Bit().constData() << +// "to go to" << userString << "\n"; +// +// NodeList::getInstance()->setDomainHostname(valueList[i]); +// +// // orient the user to face the target +// glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(), +// orientationItems[1].toFloat(), orientationItems[2].toFloat()))) * +// glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f); +// Application::getInstance()->getAvatar()->setOrientation(newOrientation); +// +// // move the user a couple units away +// const float DISTANCE_TO_USER = 2.0f; +// glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(), +// coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; +// Application::getInstance()->getAvatar()->setPosition(newPosition); +// } + +// } else if (keyList[i] == DataServerKey::UUID) { + // this is the user's UUID - set it on the profile +// Application::getInstance()->getProfile()->setUUID(valueList[i]); + } void DataServerClient::processMessageFromDataServer(unsigned char* packetData, int numPacketBytes) { @@ -222,38 +246,24 @@ void DataServerClient::processMessageFromDataServer(unsigned char* packetData, i processSendFromDataServer(packetData, numPacketBytes); break; case PACKET_TYPE_DATA_SERVER_CONFIRM: - processConfirmFromDataServer(packetData, numPacketBytes); + processConfirmFromDataServer(packetData); break; default: break; } } -void DataServerClient::removeMatchedPacketFromMap(unsigned char* packetData, int numPacketBytes) { - for (std::map::iterator mapIterator = _unmatchedPackets.begin(); - mapIterator != _unmatchedPackets.end(); - ++mapIterator) { - if (memcmp(mapIterator->first + sizeof(PACKET_TYPE), - packetData + sizeof(PACKET_TYPE), - numPacketBytes - sizeof(PACKET_TYPE)) == 0) { - - // this is a match - remove the confirmed packet from the vector and delete associated member - // so it isn't sent back out - delete[] mapIterator->first; - _unmatchedPackets.erase(mapIterator); - - // we've matched the packet - bail out - break; - } - } +void DataServerClient::removeMatchedPacketFromMap(unsigned char* packetData) { + quint8 sequenceNumber = *(packetData + numBytesForPacketHeader(packetData)); + + // attempt to remove a packet with this sequence number from the QMap of unmatched packets + _unmatchedPackets.remove(sequenceNumber); } void DataServerClient::resendUnmatchedPackets() { - for (std::map::iterator mapIterator = _unmatchedPackets.begin(); - mapIterator != _unmatchedPackets.end(); - ++mapIterator) { + foreach (const QByteArray& packet, _unmatchedPackets) { // send the unmatched packet to the data server - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) mapIterator->first, mapIterator->second, + NodeList::getInstance()->getNodeSocket().writeDatagram(packet.data(), packet.size(), dataServerSockAddr().getAddress(), dataServerSockAddr().getPort()); } diff --git a/interface/src/DataServerClient.h b/interface/src/DataServerClient.h index 89ec944eb6..0b4819d2e9 100644 --- a/interface/src/DataServerClient.h +++ b/interface/src/DataServerClient.h @@ -11,29 +11,42 @@ #include +#include +#include #include -#include "Application.h" +#include + +class DataServerCallbackObject { +public: + virtual void processDataServerResponse(const QUuid& userUUID, const QStringList& keyList, const QStringList& valueList) = 0; +}; class DataServerClient { public: static const HifiSockAddr& dataServerSockAddr(); static void putValueForKey(const QString& key, const char* value); - static void getClientValueForKey(const QString& key); + static void getClientValueForKey(const QString& key, DataServerCallbackObject* callbackObject); - static void getValueForKeyAndUUID(const QString& key, const QUuid& uuid); - static void getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid); - static void getValuesForKeysAndUserString(const QStringList& keys, const QString& userString); + static void getValueForKeyAndUUID(const QString& key, const QUuid& uuid, DataServerCallbackObject* callbackObject); + static void getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, DataServerCallbackObject* callbackObject); + static void getValuesForKeysAndUserString(const QStringList& keys, const QString& userString, DataServerCallbackObject* callbackObject); - static void processConfirmFromDataServer(unsigned char* packetData, int numPacketBytes); - static void processSendFromDataServer(unsigned char* packetData, int numPacketBytes); static void processMessageFromDataServer(unsigned char* packetData, int numPacketBytes); - static void removeMatchedPacketFromMap(unsigned char* packetData, int numPacketBytes); + static void resendUnmatchedPackets(); + + static void setClientIdentifier(const QString& clientIdentifier) { _clientIdentifier = clientIdentifier; } private: + static void processConfirmFromDataServer(unsigned char* packetData); + static void processSendFromDataServer(unsigned char* packetData, int numPacketBytes); + static void removeMatchedPacketFromMap(unsigned char* packetData); - static std::map _unmatchedPackets; + static QString _clientIdentifier; + static QMap _unmatchedPackets; + static QMap _callbackObjects; + static quint8 _sequenceNumber; }; namespace DataServerKey { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7299615ecc..8c33ead8a5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -963,7 +963,7 @@ void Menu::goToUser() { // there's a username entered by the user, make a request to the data-server DataServerClient::getValuesForKeysAndUserString( QStringList() << DataServerKey::Domain << DataServerKey::Position << DataServerKey::Orientation, - userDialog.textValue()); + userDialog.textValue(), Application::getInstance()->getProfile()); } sendFakeEnterEvent(); diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index 316ca195c9..68c38dd022 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -8,24 +8,28 @@ #include +#include #include +#include "Application.h" #include "Profile.h" -#include "DataServerClient.h" +#include "Util.h" Profile::Profile(const QString &username) : - _username(username), + _username(), _uuid(), _lastDomain(), _lastPosition(0.0, 0.0, 0.0), _lastOrientationSend(0), _faceModelURL() { - if (!_username.isEmpty()) { + if (!username.isEmpty()) { + setUsername(username); + // we've been given a new username, ask the data-server for profile - DataServerClient::getClientValueForKey(DataServerKey::UUID); - DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL); - DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL); + DataServerClient::getClientValueForKey(DataServerKey::UUID, this); + DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL, this); + DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL, this); // send our current domain server to the data-server updateDomain(NodeList::getInstance()->getDomainHostname()); @@ -43,6 +47,8 @@ QString Profile::getUserString() const { void Profile::setUUID(const QUuid& uuid) { _uuid = uuid; + DataServerClient::setClientIdentifier(_username); + // when the UUID is changed we need set it appropriately on our avatar instance Application::getInstance()->getAvatar()->setUUID(_uuid); } @@ -132,10 +138,19 @@ void Profile::saveData(QSettings* settings) { void Profile::loadData(QSettings* settings) { settings->beginGroup("Profile"); - _username = settings->value("username").toString(); + setUsername(settings->value("username").toString()); this->setUUID(settings->value("UUID").toUuid()); _faceModelURL = settings->value("faceModelURL").toUrl(); _skeletonModelURL = settings->value("skeletonModelURL").toUrl(); settings->endGroup(); } + +void Profile::processDataServerResponse(const QUuid& userUUID, const QStringList& keyList, const QStringList& valueList) { + qDebug() << "Called with" << keyList << valueList; +} + +void Profile::setUsername(const QString& username) { + _username = username; + DataServerClient::setClientIdentifier(_username); +} diff --git a/interface/src/avatar/Profile.h b/interface/src/avatar/Profile.h index 06f8de94a3..9d301157fe 100644 --- a/interface/src/avatar/Profile.h +++ b/interface/src/avatar/Profile.h @@ -18,7 +18,9 @@ #include #include -class Profile { +#include "DataServerClient.h" + +class Profile : public DataServerCallbackObject { public: Profile(const QString& username); @@ -44,7 +46,12 @@ public: void saveData(QSettings* settings); void loadData(QSettings* settings); + + void processDataServerResponse(const QUuid& userUUID, const QStringList& keyList, const QStringList& valueList); private: + + void setUsername(const QString& username); + QString _username; QUuid _uuid; QString _lastDomain; From 8930cf889b19b812238e5d0475bec4121d3c3733 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Jan 2014 14:48:14 -0800 Subject: [PATCH 37/58] bump data server packet version to 1 --- libraries/shared/src/PacketHeaders.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index 0d468f0e1d..eb4d6dfed9 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -55,7 +55,13 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { case PACKET_TYPE_PING_REPLY: return 1; - + + case PACKET_TYPE_DATA_SERVER_GET: + case PACKET_TYPE_DATA_SERVER_PUT: + case PACKET_TYPE_DATA_SERVER_SEND: + case PACKET_TYPE_DATA_SERVER_CONFIRM: + return 1; + default: return 0; } From 657589fa64361c02c56260f26def802ab7d3f758 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Jan 2014 14:52:45 -0800 Subject: [PATCH 38/58] add sequence number handling to the data-server --- data-server/src/DataServer.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp index 7dbfdd2c07..166c0c3255 100644 --- a/data-server/src/DataServer.cpp +++ b/data-server/src/DataServer.cpp @@ -64,6 +64,10 @@ void DataServer::readPendingDatagrams() { packetVersionMatch(packetData)) { int readBytes = numBytesForPacketHeader(packetData); + + // pull the sequence number used for this packet + quint16 sequenceNumber = *reinterpret_cast(packetData + readBytes); + readBytes += sizeof(sequenceNumber); // pull the UUID that we will need as part of the key QString uuidString(reinterpret_cast(packetData + readBytes)); @@ -80,7 +84,6 @@ void DataServer::readPendingDatagrams() { redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(username)); if (reply->type == REDIS_REPLY_STRING) { - parsedUUID = QUuid(QString(reply->str)); } @@ -120,7 +123,6 @@ void DataServer::readPendingDatagrams() { if (reply->type == REDIS_REPLY_STATUS && strcmp("OK", reply->str) == 0) { // if redis stored the value successfully reply back with a confirm - // which is the sent packet with the header replaced packetData[0] = PACKET_TYPE_DATA_SERVER_CONFIRM; _socket.writeDatagram(reinterpret_cast(packetData), receivedBytes, @@ -135,6 +137,9 @@ void DataServer::readPendingDatagrams() { numSendPacketBytes += populateTypeAndVersion(packetData, PACKET_TYPE_DATA_SERVER_SEND); + memcpy(packetData + numSendPacketBytes, &sequenceNumber, sizeof(sequenceNumber)); + numSendPacketBytes += sizeof(sequenceNumber); + const char MULTI_KEY_VALUE_SEPARATOR = '|'; if (strcmp((char*) packetData + readBytes, "uuid") != 0) { From cf40fb0ef8f21f76994def1952a46c832a1f8b27 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Jan 2014 16:55:14 -0800 Subject: [PATCH 39/58] repairs to new Qt'ed DataServer and DataServerClient --- data-server/src/DataServer.cpp | 27 ++++++++++++++------------- data-server/src/main.cpp | 4 ++++ interface/src/DataServerClient.cpp | 16 ++++++---------- interface/src/DataServerClient.h | 2 +- interface/src/avatar/Profile.cpp | 2 +- interface/src/avatar/Profile.h | 2 +- 6 files changed, 27 insertions(+), 26 deletions(-) diff --git a/data-server/src/DataServer.cpp b/data-server/src/DataServer.cpp index 166c0c3255..2916403c97 100644 --- a/data-server/src/DataServer.cpp +++ b/data-server/src/DataServer.cpp @@ -66,7 +66,8 @@ void DataServer::readPendingDatagrams() { int readBytes = numBytesForPacketHeader(packetData); // pull the sequence number used for this packet - quint16 sequenceNumber = *reinterpret_cast(packetData + readBytes); + quint8 sequenceNumber = 0; + memcpy(&sequenceNumber, packetData + readBytes, sizeof(sequenceNumber)); readBytes += sizeof(sequenceNumber); // pull the UUID that we will need as part of the key @@ -77,9 +78,8 @@ void DataServer::readPendingDatagrams() { // we failed to parse a UUID, this means the user has sent us a username QString username(reinterpret_cast(packetData + readBytes)); - readBytes += sizeof(username) + sizeof('\0'); - - + readBytes += username.size() + sizeof('\0'); + // ask redis for the UUID for this user redisReply* reply = (redisReply*) redisCommand(_redis, "GET user:%s", qPrintable(username)); @@ -113,7 +113,7 @@ void DataServer::readPendingDatagrams() { QString dataValue(reinterpret_cast(packetData + readBytes)); readBytes += dataValue.size() + sizeof('\0'); - qDebug("Sending command to redis: SET uuid:%s:%s %s\n", + qDebug("Sending command to redis: SET uuid:%s:%s %s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey), qPrintable(dataValue)); @@ -133,19 +133,15 @@ void DataServer::readPendingDatagrams() { } else { // setup a send packet with the returned data // leverage the packetData sent by overwriting and appending - int numSendPacketBytes = 0; + int numSendPacketBytes = receivedBytes; - numSendPacketBytes += populateTypeAndVersion(packetData, PACKET_TYPE_DATA_SERVER_SEND); - - memcpy(packetData + numSendPacketBytes, &sequenceNumber, sizeof(sequenceNumber)); - numSendPacketBytes += sizeof(sequenceNumber); + packetData[0] = PACKET_TYPE_DATA_SERVER_SEND; const char MULTI_KEY_VALUE_SEPARATOR = '|'; if (strcmp((char*) packetData + readBytes, "uuid") != 0) { // the user has sent one or more keys - make the associated requests - for (int j = 0; j < numKeys; j++) { // pull the key that specifies the data the user is putting/getting, null terminate it @@ -160,7 +156,7 @@ void DataServer::readPendingDatagrams() { QString dataKey(QByteArray(reinterpret_cast(packetData + readBytes), numDataKeyBytes)); readBytes += dataKey.size() + sizeof('\0'); - qDebug("Sending command to redis: GET uuid:%s:%s\n", + qDebug("Sending command to redis: GET uuid:%s:%s", qPrintable(uuidStringWithoutCurlyBraces(parsedUUID)), qPrintable(dataKey)); redisReply* reply = (redisReply*) redisCommand(_redis, "GET uuid:%s:%s", @@ -182,16 +178,21 @@ void DataServer::readPendingDatagrams() { freeReplyObject(reply); } + + // null terminate the packet we're sending back (erases the trailing separator) + packetData[(numSendPacketBytes - 1)] = '\0'; } else { // user is asking for a UUID matching username, copy the UUID we found QString uuidString = uuidStringWithoutCurlyBraces(parsedUUID); memcpy(packetData + numSendPacketBytes, uuidString.constData(), uuidString.size() + sizeof('\0')); - numSendPacketBytes = uuidString.size() + sizeof('\0'); + numSendPacketBytes += uuidString.size() + sizeof('\0'); } // reply back with the send packet _socket.writeDatagram(reinterpret_cast(packetData), numSendPacketBytes, senderAddress, senderPort); + + } } } diff --git a/data-server/src/main.cpp b/data-server/src/main.cpp index 8e71789032..dc99de70b8 100755 --- a/data-server/src/main.cpp +++ b/data-server/src/main.cpp @@ -12,12 +12,16 @@ #include "DataServer.h" +const char DATA_SERVER_LOGGING_TARGET_NAME[] = "data-server"; + int main(int argc, char* argv[]) { setvbuf(stdout, NULL, _IOLBF, 0); qInstallMessageHandler(Logging::verboseMessageHandler); + Logging::setTargetName(DATA_SERVER_LOGGING_TARGET_NAME); + DataServer dataServer(argc, argv); return dataServer.exec(); diff --git a/interface/src/DataServerClient.cpp b/interface/src/DataServerClient.cpp index af76d8eea2..ee2b294440 100644 --- a/interface/src/DataServerClient.cpp +++ b/interface/src/DataServerClient.cpp @@ -24,7 +24,7 @@ quint8 DataServerClient::_sequenceNumber = 0; const char MULTI_KEY_VALUE_SEPARATOR = '|'; -const char DATA_SERVER_HOSTNAME[] = "data.highfidelity.io"; +const char DATA_SERVER_HOSTNAME[] = "localhost"; const unsigned short DATA_SERVER_PORT = 3282; const HifiSockAddr& DataServerClient::dataServerSockAddr() { @@ -35,7 +35,7 @@ const HifiSockAddr& DataServerClient::dataServerSockAddr() { void DataServerClient::putValueForKey(const QString& key, const char* value) { if (!_clientIdentifier.isEmpty()) { - static unsigned char putPacket[MAX_PACKET_SIZE]; + unsigned char putPacket[MAX_PACKET_SIZE]; // setup the header for this packet int numPacketBytes = populateTypeAndVersion(putPacket, PACKET_TYPE_DATA_SERVER_PUT); @@ -88,7 +88,7 @@ void DataServerClient::getValuesForKeysAndUUID(const QStringList& keys, const QU void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, const QString& userString, DataServerCallbackObject* callbackObject) { if (!userString.isEmpty() && keys.size() <= UCHAR_MAX) { - static unsigned char getPacket[MAX_PACKET_SIZE]; + unsigned char getPacket[MAX_PACKET_SIZE]; // setup the header for this packet int numPacketBytes = populateTypeAndVersion(getPacket, PACKET_TYPE_DATA_SERVER_GET); @@ -146,19 +146,15 @@ void DataServerClient::processSendFromDataServer(unsigned char* packetData, int DataServerCallbackObject* callbackObject = _callbackObjects.take(_sequenceNumber); char* userStringPosition = (char*) packetData + numHeaderBytes + sizeof(_sequenceNumber); + QString userString(userStringPosition); - QString userString(QByteArray(userStringPosition, strlen(userStringPosition))); - - QUuid userUUID(userString); - - char* keysPosition = (char*) packetData + numHeaderBytes + strlen(userStringPosition) - + sizeof('\0') + sizeof(unsigned char); + char* keysPosition = userStringPosition + userString.size() + sizeof('\0') + sizeof(unsigned char); char* valuesPosition = keysPosition + strlen(keysPosition) + sizeof('\0'); QStringList keyList = QString(keysPosition).split(MULTI_KEY_VALUE_SEPARATOR); QStringList valueList = QString(valuesPosition).split(MULTI_KEY_VALUE_SEPARATOR); - callbackObject->processDataServerResponse(userUUID, keyList, valueList); + callbackObject->processDataServerResponse(userString, keyList, valueList); } diff --git a/interface/src/DataServerClient.h b/interface/src/DataServerClient.h index 0b4819d2e9..a25bd3faa2 100644 --- a/interface/src/DataServerClient.h +++ b/interface/src/DataServerClient.h @@ -19,7 +19,7 @@ class DataServerCallbackObject { public: - virtual void processDataServerResponse(const QUuid& userUUID, const QStringList& keyList, const QStringList& valueList) = 0; + virtual void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList) = 0; }; class DataServerClient { diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index 68c38dd022..f455a9fdb6 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -146,7 +146,7 @@ void Profile::loadData(QSettings* settings) { settings->endGroup(); } -void Profile::processDataServerResponse(const QUuid& userUUID, const QStringList& keyList, const QStringList& valueList) { +void Profile::processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList) { qDebug() << "Called with" << keyList << valueList; } diff --git a/interface/src/avatar/Profile.h b/interface/src/avatar/Profile.h index 9d301157fe..225022b99d 100644 --- a/interface/src/avatar/Profile.h +++ b/interface/src/avatar/Profile.h @@ -47,7 +47,7 @@ public: void saveData(QSettings* settings); void loadData(QSettings* settings); - void processDataServerResponse(const QUuid& userUUID, const QStringList& keyList, const QStringList& valueList); + void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList); private: void setUsername(const QString& username); From 0ac5ec0690629672dac1129c07785fb933eb8d28 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Jan 2014 17:05:31 -0800 Subject: [PATCH 40/58] hook old data server functionality through profile --- interface/src/avatar/Profile.cpp | 75 +++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index f455a9fdb6..ebe04ef476 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -147,7 +147,80 @@ void Profile::loadData(QSettings* settings) { } void Profile::processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList) { - qDebug() << "Called with" << keyList << valueList; + for (int i = 0; i < keyList.size(); i++) { + if (valueList[i] != " ") { + if (keyList[i] == DataServerKey::FaceMeshURL) { + if (userString == _username || userString == uuidStringWithoutCurlyBraces(_uuid)) { + qDebug("Changing user's face model URL to %s", valueList[i].toLocal8Bit().constData()); + Application::getInstance()->getProfile()->setFaceModelURL(QUrl(valueList[i])); + } else { + // mesh URL for a UUID, find avatar in our list + + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { + Avatar* avatar = (Avatar *) node->getLinkedData(); + + if (avatar->getUUID() == QUuid(userString)) { + QMetaObject::invokeMethod(&avatar->getHead().getFaceModel(), + "setURL", Q_ARG(QUrl, QUrl(valueList[i]))); + } + } + } + } + } else if (keyList[i] == DataServerKey::SkeletonURL) { + if (userString == _username || userString == uuidStringWithoutCurlyBraces(_uuid)) { + qDebug("Changing user's skeleton URL to %s", valueList[i].toLocal8Bit().constData()); + Application::getInstance()->getProfile()->setSkeletonModelURL(QUrl(valueList[i])); + } else { + // skeleton URL for a UUID, find avatar in our list + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { + Avatar* avatar = (Avatar *) node->getLinkedData(); + + if (avatar->getUUID() == QUuid(userString)) { + QMetaObject::invokeMethod(&avatar->getSkeletonModel(), "setURL", + Q_ARG(QUrl, QUrl(valueList[i]))); + } + } + } + } + } else if (keyList[i] == DataServerKey::Domain && keyList[i + 1] == DataServerKey::Position && + keyList[i + 2] == DataServerKey::Orientation && valueList[i] != " " && + valueList[i + 1] != " " && valueList[i + 2] != " ") { + + QStringList coordinateItems = valueList[i + 1].split(','); + QStringList orientationItems = valueList[i + 2].split(','); + + if (coordinateItems.size() == 3 && orientationItems.size() == 3) { + + // send a node kill request, indicating to other clients that they should play the "disappeared" effect + NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); + + qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() << + ", position to" << valueList[i + 1].toLocal8Bit().constData() << + ", and orientation to" << valueList[i + 2].toLocal8Bit().constData() << + "to go to" << userString; + + NodeList::getInstance()->setDomainHostname(valueList[i]); + // orient the user to face the target + glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(), + orientationItems[1].toFloat(), orientationItems[2].toFloat()))) * + glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f); + Application::getInstance()->getAvatar()->setOrientation(newOrientation); + + // move the user a couple units away + const float DISTANCE_TO_USER = 2.0f; + glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(), + coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; + Application::getInstance()->getAvatar()->setPosition(newPosition); + } + + } else if (keyList[i] == DataServerKey::UUID) { + // this is the user's UUID - set it on the profile + _uuid = QUuid(valueList[i]); + } + } + } } void Profile::setUsername(const QString& username) { From 80fcc8743747ad7b466ffbc26b3e703e0586130a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Jan 2014 17:09:03 -0800 Subject: [PATCH 41/58] some spacing fixes in Profile --- interface/src/avatar/Profile.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index ebe04ef476..d32f0f018e 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -204,14 +204,17 @@ void Profile::processDataServerResponse(const QString& userString, const QString NodeList::getInstance()->setDomainHostname(valueList[i]); // orient the user to face the target glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(), - orientationItems[1].toFloat(), orientationItems[2].toFloat()))) * - glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f); - Application::getInstance()->getAvatar()->setOrientation(newOrientation); + orientationItems[1].toFloat(), + orientationItems[2].toFloat()))) * + glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f); + Application::getInstance()->getAvatar() + ->setOrientation(newOrientation); // move the user a couple units away const float DISTANCE_TO_USER = 2.0f; glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(), - coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; + coordinateItems[2].toFloat() + ) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; Application::getInstance()->getAvatar()->setPosition(newPosition); } From d9ae27953bc3c006a149b667d1eb09a1ac1b1489 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Jan 2014 17:12:52 -0800 Subject: [PATCH 42/58] move DataServerClient to shared for use in other targets --- .../shared}/src/DataServerClient.cpp | 94 ++----------------- .../shared}/src/DataServerClient.h | 2 +- 2 files changed, 9 insertions(+), 87 deletions(-) rename {interface => libraries/shared}/src/DataServerClient.cpp (60%) rename {interface => libraries/shared}/src/DataServerClient.h (98%) diff --git a/interface/src/DataServerClient.cpp b/libraries/shared/src/DataServerClient.cpp similarity index 60% rename from interface/src/DataServerClient.cpp rename to libraries/shared/src/DataServerClient.cpp index ee2b294440..40c75444f6 100644 --- a/interface/src/DataServerClient.cpp +++ b/libraries/shared/src/DataServerClient.cpp @@ -9,11 +9,9 @@ #include #include -#include -#include -#include - -#include "avatar/Profile.h" +#include "NodeList.h" +#include "PacketHeaders.h" +#include "UUID.h" #include "DataServerClient.h" @@ -140,12 +138,14 @@ void DataServerClient::processSendFromDataServer(unsigned char* packetData, int // pull the user string from the packet so we know who to associate this with int numHeaderBytes = numBytesForPacketHeader(packetData); - _sequenceNumber = *(packetData + numHeaderBytes); + quint8 sequenceNumber = *(packetData + numHeaderBytes); if (_callbackObjects.find(_sequenceNumber) != _callbackObjects.end()) { - DataServerCallbackObject* callbackObject = _callbackObjects.take(_sequenceNumber); + // remove the packet from our two maps, it's matched + DataServerCallbackObject* callbackObject = _callbackObjects.take(sequenceNumber); + _unmatchedPackets.remove(sequenceNumber); - char* userStringPosition = (char*) packetData + numHeaderBytes + sizeof(_sequenceNumber); + char* userStringPosition = (char*) packetData + numHeaderBytes + sizeof(sequenceNumber); QString userString(userStringPosition); char* keysPosition = userStringPosition + userString.size() + sizeof('\0') + sizeof(unsigned char); @@ -156,84 +156,6 @@ void DataServerClient::processSendFromDataServer(unsigned char* packetData, int callbackObject->processDataServerResponse(userString, keyList, valueList); } - - - // user string was UUID, find matching avatar and associate data -// for (int i = 0; i < keyList.size(); i++) { -// if (valueList[i] != " ") { -// if (keyList[i] == DataServerKey::FaceMeshURL) { - -// if (userUUID.isNull() || userUUID == Application::getInstance()->getProfile()->getUUID()) { -// qDebug("Changing user's face model URL to %s\n", valueList[i].toLocal8Bit().constData()); -// Application::getInstance()->getProfile()->setFaceModelURL(QUrl(valueList[i])); -// } else { -// // mesh URL for a UUID, find avatar in our list -// -// foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { -// if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { -// Avatar* avatar = (Avatar *) node->getLinkedData(); -// -// if (avatar->getUUID() == userUUID) { -// QMetaObject::invokeMethod(&avatar->getHead().getFaceModel(), -// "setURL", Q_ARG(QUrl, QUrl(valueList[i]))); -// } -// } -// } -// } -// } else if (keyList[i] == DataServerKey::SkeletonURL) { - -// if (userUUID.isNull() || userUUID == Application::getInstance()->getProfile()->getUUID()) { -// qDebug("Changing user's skeleton URL to %s\n", valueList[i].toLocal8Bit().constData()); -// Application::getInstance()->getProfile()->setSkeletonModelURL(QUrl(valueList[i])); -// } else { -// // skeleton URL for a UUID, find avatar in our list -// foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { -// if (node->getLinkedData() != NULL && node->getType() == NODE_TYPE_AGENT) { -// Avatar* avatar = (Avatar *) node->getLinkedData(); -// -// if (avatar->getUUID() == userUUID) { -// QMetaObject::invokeMethod(&avatar->getSkeletonModel(), "setURL", -// Q_ARG(QUrl, QUrl(valueList[i]))); -// } -// } -// } -// } -// } else if (keyList[i] == DataServerKey::Domain && keyList[i + 1] == DataServerKey::Position && -// keyList[i + 2] == DataServerKey::Orientation && valueList[i] != " " && -// valueList[i + 1] != " " && valueList[i + 2] != " ") { -// -// QStringList coordinateItems = valueList[i + 1].split(','); -// QStringList orientationItems = valueList[i + 2].split(','); - -// if (coordinateItems.size() == 3 && orientationItems.size() == 3) { -// -// // send a node kill request, indicating to other clients that they should play the "disappeared" effect -// NodeList::getInstance()->sendKillNode(&NODE_TYPE_AVATAR_MIXER, 1); -// -// qDebug() << "Changing domain to" << valueList[i].toLocal8Bit().constData() << -// ", position to" << valueList[i + 1].toLocal8Bit().constData() << -// ", and orientation to" << valueList[i + 2].toLocal8Bit().constData() << -// "to go to" << userString << "\n"; -// -// NodeList::getInstance()->setDomainHostname(valueList[i]); -// -// // orient the user to face the target -// glm::quat newOrientation = glm::quat(glm::radians(glm::vec3(orientationItems[0].toFloat(), -// orientationItems[1].toFloat(), orientationItems[2].toFloat()))) * -// glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f); -// Application::getInstance()->getAvatar()->setOrientation(newOrientation); -// -// // move the user a couple units away -// const float DISTANCE_TO_USER = 2.0f; -// glm::vec3 newPosition = glm::vec3(coordinateItems[0].toFloat(), coordinateItems[1].toFloat(), -// coordinateItems[2].toFloat()) - newOrientation * IDENTITY_FRONT * DISTANCE_TO_USER; -// Application::getInstance()->getAvatar()->setPosition(newPosition); -// } - -// } else if (keyList[i] == DataServerKey::UUID) { - // this is the user's UUID - set it on the profile -// Application::getInstance()->getProfile()->setUUID(valueList[i]); - } void DataServerClient::processMessageFromDataServer(unsigned char* packetData, int numPacketBytes) { diff --git a/interface/src/DataServerClient.h b/libraries/shared/src/DataServerClient.h similarity index 98% rename from interface/src/DataServerClient.h rename to libraries/shared/src/DataServerClient.h index a25bd3faa2..0ebf90f26b 100644 --- a/interface/src/DataServerClient.h +++ b/libraries/shared/src/DataServerClient.h @@ -15,7 +15,7 @@ #include #include -#include +#include "HifiSockAddr.h" class DataServerCallbackObject { public: From 340ae8ebd1f3f73fe9471da35686f4d917e03227 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Jan 2014 17:17:26 -0800 Subject: [PATCH 43/58] column width repair --- libraries/shared/src/DataServerClient.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/DataServerClient.h b/libraries/shared/src/DataServerClient.h index 0ebf90f26b..734d42ed59 100644 --- a/libraries/shared/src/DataServerClient.h +++ b/libraries/shared/src/DataServerClient.h @@ -30,7 +30,8 @@ public: static void getValueForKeyAndUUID(const QString& key, const QUuid& uuid, DataServerCallbackObject* callbackObject); static void getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, DataServerCallbackObject* callbackObject); - static void getValuesForKeysAndUserString(const QStringList& keys, const QString& userString, DataServerCallbackObject* callbackObject); + static void getValuesForKeysAndUserString(const QStringList& keys, const QString& userString, + DataServerCallbackObject* callbackObject); static void processMessageFromDataServer(unsigned char* packetData, int numPacketBytes); From b972d4260cd38e2b7dff7c804e84229fa7c6e0fd Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 21 Jan 2014 10:31:16 -0600 Subject: [PATCH 44/58] PR Fixes --- interface/src/Application.cpp | 7 ++++--- interface/src/ui/UpdateDialog.cpp | 19 +++++++------------ interface/src/ui/UpdateDialog.h | 9 +++------ 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5b87fd470d..52c54c8b3d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -203,7 +203,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); - qDebug("[VERSION] Build sequence: %s\n", applicationVersion().toStdString().c_str()); + qDebug() << "[VERSION] Build sequence: " << qPrintable(applicationVersion()); _settings = new QSettings(this); @@ -4426,6 +4426,7 @@ void Application::parseVersionXml() { QString releaseDate; QString releaseNotes; QString latestVersion; + QUrl downloadUrl; QXmlStreamReader xml(_latestVersionReply); while (!xml.atEnd() && !xml.hasError()) { @@ -4446,13 +4447,13 @@ void Application::parseVersionXml() { } if (xml.name() == operatingSystem) { xml.readNext(); - _downloadUrl = QUrl(xml.text().toString()); + downloadUrl = QUrl(xml.text().toString()); } } } if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) { - new UpdateDialog(_glWidget, releaseNotes, latestVersion, _downloadUrl); + new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl); } delete _latestVersionReply; diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index ae3a52932f..9bf86dd015 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -19,10 +19,11 @@ #include "SharedUtil.h" #include "UpdateDialog.h" -UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latestVersion, QUrl downloadURL) : +UpdateDialog::UpdateDialog(QWidget *parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL) : QWidget(parent, Qt::Widget) { - _latestVersion = new QString(latestVersion); + _latestVersion = latestVersion; + _downloadUrl = downloadURL; QUiLoader updateDialogLoader; QWidget* updateDialog; @@ -46,22 +47,16 @@ UpdateDialog::UpdateDialog(QWidget *parent, QString releaseNotes, QString latest connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload())); connect(skipButton, SIGNAL(released()), this, SLOT(handleSkip())); - connect(closeButton, SIGNAL(released()), this, SLOT(handleClose())); + connect(closeButton, SIGNAL(released()), this, SLOT(close())); updateDialog->show(); } void UpdateDialog::handleDownload() { - Application* application = Application::getInstance(); - QDesktopServices::openUrl(application->_downloadUrl); - application->quit(); + QDesktopServices::openUrl(_downloadUrl); + Application::getInstance()->quit(); } void UpdateDialog::handleSkip() { - Application* application = Application::getInstance(); - application->skipVersion(*_latestVersion); - this->close(); -} - -void UpdateDialog::handleClose() { + Application::getInstance()->skipVersion(_latestVersion); this->close(); } \ No newline at end of file diff --git a/interface/src/ui/UpdateDialog.h b/interface/src/ui/UpdateDialog.h index aa0b800528..1c4f5de9d0 100644 --- a/interface/src/ui/UpdateDialog.h +++ b/interface/src/ui/UpdateDialog.h @@ -9,24 +9,21 @@ #ifndef __hifi__UpdateDialog__ #define __hifi__UpdateDialog__ -#include #include -#include - class UpdateDialog : public QWidget { Q_OBJECT public: - UpdateDialog(QWidget* parent, QString releaseNotes, QString latestVersion, QUrl downloadURL); + UpdateDialog(QWidget* parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL); private: - QString* _latestVersion; + QString _latestVersion; + QUrl _downloadUrl; private slots: void handleDownload(); void handleSkip(); - void handleClose(); }; #endif /* defined(__hifi__UpdateDialog__) */ From cc147bc8bfbde559eadb8677b28dc2b0a5450310 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 21 Jan 2014 10:39:29 -0600 Subject: [PATCH 45/58] PR fixes --- interface/src/Application.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 52c54c8b3d..1a12bdd85b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4402,14 +4402,16 @@ void Application::updateLocalOctreeCache(bool firstTime) { } void Application::checkVersion() { + qDebug() << "################### in check version"; QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL))); latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); _latestVersionReply = Application::getInstance()->getNetworkAccessManager()->get(latestVersionRequest); - connect(_latestVersionReply, SIGNAL(readyRead()), SLOT(parseVersionXml())); + connect(_latestVersionReply, SIGNAL(finished()), SLOT(parseVersionXml())); } void Application::parseVersionXml() { + qDebug() << "################### in parse version xml"; #ifdef Q_OS_WIN32 QString operatingSystem("win"); @@ -4463,7 +4465,7 @@ bool Application::shouldSkipVersion(QString latestVersion) { QFile skipFile(SKIP_FILENAME); skipFile.open(QIODevice::ReadWrite); QString skipVersion(skipFile.readAll()); - return (skipVersion == latestVersion || applicationVersion() == "dev"); + return (skipVersion == latestVersion /*|| applicationVersion() == "dev"*/); } void Application::skipVersion(QString latestVersion) { From b2ef07f4856a0c8571ecce76303a060d5f9768aa Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 21 Jan 2014 11:17:43 -0600 Subject: [PATCH 46/58] PR Fixes --- interface/src/Application.cpp | 12 +++--------- interface/src/Application.h | 2 -- interface/src/ui/UpdateDialog.cpp | 9 ++++----- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1a12bdd85b..8c48dfd378 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -4402,16 +4403,12 @@ void Application::updateLocalOctreeCache(bool firstTime) { } void Application::checkVersion() { - qDebug() << "################### in check version"; QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL))); latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); - _latestVersionReply = Application::getInstance()->getNetworkAccessManager()->get(latestVersionRequest); - - connect(_latestVersionReply, SIGNAL(finished()), SLOT(parseVersionXml())); + connect(Application::getInstance()->getNetworkAccessManager()->get(latestVersionRequest), SIGNAL(finished()), SLOT(parseVersionXml())); } void Application::parseVersionXml() { - qDebug() << "################### in parse version xml"; #ifdef Q_OS_WIN32 QString operatingSystem("win"); @@ -4430,7 +4427,7 @@ void Application::parseVersionXml() { QString latestVersion; QUrl downloadUrl; - QXmlStreamReader xml(_latestVersionReply); + QXmlStreamReader xml(qobject_cast(QObject::sender())); while (!xml.atEnd() && !xml.hasError()) { QXmlStreamReader::TokenType token = xml.readNext(); @@ -4453,12 +4450,9 @@ void Application::parseVersionXml() { } } } - if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) { new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl); } - - delete _latestVersionReply; } bool Application::shouldSkipVersion(QString latestVersion) { diff --git a/interface/src/Application.h b/interface/src/Application.h index d1d5047db2..ae2638c230 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -208,7 +208,6 @@ public: void setHighlightVoxel(const VoxelDetail& highlightVoxel) { _highlightVoxel = highlightVoxel; } void setIsHighlightVoxel(bool isHighlightVoxel) { _isHighlightVoxel = isHighlightVoxel; } - QUrl _downloadUrl; void skipVersion(QString latestVersion); public slots: @@ -512,7 +511,6 @@ private: void checkVersion(); void displayUpdateDialog(); bool shouldSkipVersion(QString latestVersion); - QNetworkReply* _latestVersionReply; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/ui/UpdateDialog.cpp b/interface/src/ui/UpdateDialog.cpp index 9bf86dd015..12c5cd83e0 100644 --- a/interface/src/ui/UpdateDialog.cpp +++ b/interface/src/ui/UpdateDialog.cpp @@ -20,10 +20,9 @@ #include "UpdateDialog.h" UpdateDialog::UpdateDialog(QWidget *parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL) : - QWidget(parent, Qt::Widget) { - - _latestVersion = latestVersion; - _downloadUrl = downloadURL; + QWidget(parent, Qt::Widget), + _latestVersion(latestVersion), + _downloadUrl(downloadURL) { QUiLoader updateDialogLoader; QWidget* updateDialog; @@ -36,7 +35,7 @@ UpdateDialog::UpdateDialog(QWidget *parent, const QString& releaseNotes, const Q .arg(Application::getInstance()->applicationVersion(), latestVersion); - setAttribute(Qt::WA_DeleteOnClose); + updateDialog->setAttribute(Qt::WA_DeleteOnClose); QPushButton* downloadButton = updateDialog->findChild("downloadButton"); QPushButton* skipButton = updateDialog->findChild("skipButton"); From 18a6d7416534c478e1ed917bb1e37c6180f40d65 Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 21 Jan 2014 11:24:23 -0600 Subject: [PATCH 47/58] removing comment --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8c48dfd378..044dfdbb74 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4459,7 +4459,7 @@ bool Application::shouldSkipVersion(QString latestVersion) { QFile skipFile(SKIP_FILENAME); skipFile.open(QIODevice::ReadWrite); QString skipVersion(skipFile.readAll()); - return (skipVersion == latestVersion /*|| applicationVersion() == "dev"*/); + return (skipVersion == latestVersion || applicationVersion() == "dev"); } void Application::skipVersion(QString latestVersion) { From a4c057b01fe0487c0623073a110b0855971129cc Mon Sep 17 00:00:00 2001 From: Leonardo Murillo Date: Tue, 21 Jan 2014 11:27:03 -0600 Subject: [PATCH 48/58] PR fix --- interface/src/Application.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 044dfdbb74..2b75763875 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4426,8 +4426,9 @@ void Application::parseVersionXml() { QString releaseNotes; QString latestVersion; QUrl downloadUrl; + QObject* sender = QObject::sender(); - QXmlStreamReader xml(qobject_cast(QObject::sender())); + QXmlStreamReader xml(qobject_cast(sender)); while (!xml.atEnd() && !xml.hasError()) { QXmlStreamReader::TokenType token = xml.readNext(); @@ -4453,6 +4454,7 @@ void Application::parseVersionXml() { if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) { new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl); } + sender->deleteLater(); } bool Application::shouldSkipVersion(QString latestVersion) { From 3613773b7993cd53b5fedab6bc64ec22dc0b2411 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Jan 2014 09:55:06 -0800 Subject: [PATCH 49/58] cleanup DSC api, add scripting interface --- interface/src/Application.cpp | 4 +- interface/src/Menu.cpp | 12 +- interface/src/avatar/Profile.cpp | 17 ++- .../src/DataServerScriptingInterface.cpp | 21 ++++ .../src/DataServerScriptingInterface.h | 26 +++++ libraries/shared/src/DataServerClient.cpp | 103 +++++++++--------- libraries/shared/src/DataServerClient.h | 15 ++- 7 files changed, 121 insertions(+), 77 deletions(-) create mode 100644 libraries/script-engine/src/DataServerScriptingInterface.cpp create mode 100644 libraries/script-engine/src/DataServerScriptingInterface.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4b8343e72f..8b0fcc9b09 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1854,8 +1854,8 @@ void Application::init() { if (!_profile.getUsername().isEmpty()) { // we have a username for this avatar, ask the data-server for the mesh URL for this avatar - DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL, &_profile); - DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL, &_profile); + DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL, _profile.getUsername(), &_profile); + DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL, _profile.getUsername(), &_profile); } // Set up VoxelSystem after loading preferences so we can get the desired max voxel count diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8c33ead8a5..a4fcd975ca 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -830,8 +830,9 @@ void Menu::editPreferences() { applicationInstance->getProfile()->setFaceModelURL(faceModelURL); // send the new face mesh URL to the data-server (if we have a client UUID) - DataServerClient::putValueForKey(DataServerKey::FaceMeshURL, - faceModelURL.toString().toLocal8Bit().constData()); + DataServerClient::putValueForKeyAndUsername(DataServerKey::FaceMeshURL, + faceModelURL.toString().toLocal8Bit().constData(), + applicationInstance->getProfile()->getUsername()); } QUrl skeletonModelURL(skeletonURLEdit->text()); @@ -841,8 +842,9 @@ void Menu::editPreferences() { applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL); // send the new skeleton model URL to the data-server (if we have a client UUID) - DataServerClient::putValueForKey(DataServerKey::SkeletonURL, - skeletonModelURL.toString().toLocal8Bit().constData()); + DataServerClient::putValueForKeyAndUsername(DataServerKey::SkeletonURL, + skeletonModelURL.toString().toLocal8Bit().constData(), + applicationInstance->getProfile()->getUsername()); } applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum()); @@ -961,7 +963,7 @@ void Menu::goToUser() { int dialogReturn = userDialog.exec(); if (dialogReturn == QDialog::Accepted && !userDialog.textValue().isEmpty()) { // there's a username entered by the user, make a request to the data-server - DataServerClient::getValuesForKeysAndUserString( + DataServerClient::getValuesForKeysAndUsername( QStringList() << DataServerKey::Domain << DataServerKey::Position << DataServerKey::Orientation, userDialog.textValue(), Application::getInstance()->getProfile()); } diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index d32f0f018e..f701505e73 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -27,9 +27,9 @@ Profile::Profile(const QString &username) : setUsername(username); // we've been given a new username, ask the data-server for profile - DataServerClient::getClientValueForKey(DataServerKey::UUID, this); - DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL, this); - DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL, this); + DataServerClient::getClientValueForKey(DataServerKey::UUID, username, this); + DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL, username, this); + DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL, username, this); // send our current domain server to the data-server updateDomain(NodeList::getInstance()->getDomainHostname()); @@ -47,8 +47,6 @@ QString Profile::getUserString() const { void Profile::setUUID(const QUuid& uuid) { _uuid = uuid; - DataServerClient::setClientIdentifier(_username); - // when the UUID is changed we need set it appropriately on our avatar instance Application::getInstance()->getAvatar()->setUUID(_uuid); } @@ -72,7 +70,7 @@ void Profile::updateDomain(const QString& domain) { _lastDomain = domain; // send the changed domain to the data-server - DataServerClient::putValueForKey(DataServerKey::Domain, domain.toLocal8Bit().constData()); + DataServerClient::putValueForKeyAndUsername(DataServerKey::Domain, domain, _username); } } @@ -101,7 +99,8 @@ void Profile::updatePosition(const glm::vec3 position) { gettimeofday(&lastPositionSend, NULL); // send the changed position to the data-server - DataServerClient::putValueForKey(DataServerKey::Position, createByteArray(position).constData()); + DataServerClient::putValueForKeyAndUsername(DataServerKey::Position, + QString(createByteArray(position)), _username); } } } @@ -117,7 +116,8 @@ void Profile::updateOrientation(const glm::quat& orientation) { uint64_t now = usecTimestampNow(); if (now - _lastOrientationSend >= DATA_SERVER_ORIENTATION_UPDATE_INTERVAL_USECS && glm::distance(_lastOrientation, eulerAngles) >= DATA_SERVER_ORIENTATION_CHANGE_THRESHOLD_DEGREES) { - DataServerClient::putValueForKey(DataServerKey::Orientation, createByteArray(eulerAngles).constData()); + DataServerClient::putValueForKeyAndUsername(DataServerKey::Orientation, QString(createByteArray(eulerAngles)), + _username); _lastOrientation = eulerAngles; _lastOrientationSend = now; @@ -228,5 +228,4 @@ void Profile::processDataServerResponse(const QString& userString, const QString void Profile::setUsername(const QString& username) { _username = username; - DataServerClient::setClientIdentifier(_username); } diff --git a/libraries/script-engine/src/DataServerScriptingInterface.cpp b/libraries/script-engine/src/DataServerScriptingInterface.cpp new file mode 100644 index 0000000000..236f46e54c --- /dev/null +++ b/libraries/script-engine/src/DataServerScriptingInterface.cpp @@ -0,0 +1,21 @@ +// +// DataServerScriptingInterface.cpp +// hifi +// +// Created by Stephen Birarda on 1/20/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include + +#include "DataServerScriptingInterface.h" + +DataServerScriptingInterface::DataServerScriptingInterface() : + _uuid(QUuid::createUuid()) +{ + +} + +void DataServerScriptingInterface::setValueForKey(const QString& key, const QString& value) { + DataServerClient::putValueForKeyAndUUID(key, value, _uuid); +} \ No newline at end of file diff --git a/libraries/script-engine/src/DataServerScriptingInterface.h b/libraries/script-engine/src/DataServerScriptingInterface.h new file mode 100644 index 0000000000..1cba837b2c --- /dev/null +++ b/libraries/script-engine/src/DataServerScriptingInterface.h @@ -0,0 +1,26 @@ +// +// DataServerScriptingInterface.h +// hifi +// +// Created by Stephen Birarda on 1/20/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__DataServerScriptingInterface__ +#define __hifi__DataServerScriptingInterface__ + +#include +#include +#include + +class DataServerScriptingInterface : public QObject { + Q_OBJECT +public: + DataServerScriptingInterface(); +public slots: + void setValueForKey(const QString& key, const QString& value); +private: + QUuid _uuid; +}; + +#endif /* defined(__hifi__DataServerScriptingInterface__) */ diff --git a/libraries/shared/src/DataServerClient.cpp b/libraries/shared/src/DataServerClient.cpp index 40c75444f6..b16739c9bf 100644 --- a/libraries/shared/src/DataServerClient.cpp +++ b/libraries/shared/src/DataServerClient.cpp @@ -17,7 +17,6 @@ QMap DataServerClient::_unmatchedPackets; QMap DataServerClient::_callbackObjects; -QString DataServerClient::_clientIdentifier; quint8 DataServerClient::_sequenceNumber = 0; const char MULTI_KEY_VALUE_SEPARATOR = '|'; @@ -30,47 +29,48 @@ const HifiSockAddr& DataServerClient::dataServerSockAddr() { return dsSockAddr; } -void DataServerClient::putValueForKey(const QString& key, const char* value) { - if (!_clientIdentifier.isEmpty()) { - - unsigned char putPacket[MAX_PACKET_SIZE]; - - // setup the header for this packet - int numPacketBytes = populateTypeAndVersion(putPacket, PACKET_TYPE_DATA_SERVER_PUT); - - // pack the sequence number - memcpy(putPacket + numPacketBytes, &_sequenceNumber, sizeof(_sequenceNumber)); - numPacketBytes += sizeof(_sequenceNumber); - - // pack the client UUID, null terminated - memcpy(putPacket + numPacketBytes, qPrintable(_clientIdentifier), _clientIdentifier.toLocal8Bit().size()); - numPacketBytes += _clientIdentifier.toLocal8Bit().size(); - putPacket[numPacketBytes++] = '\0'; +void DataServerClient::putValueForKeyAndUsername(const QString& key, const QString& value, const QString& clientIdentifier) { + unsigned char putPacket[MAX_PACKET_SIZE]; + + // setup the header for this packet + int numPacketBytes = populateTypeAndVersion(putPacket, PACKET_TYPE_DATA_SERVER_PUT); + + // pack the sequence number + memcpy(putPacket + numPacketBytes, &_sequenceNumber, sizeof(_sequenceNumber)); + numPacketBytes += sizeof(_sequenceNumber); + + // pack the client UUID, null terminated + memcpy(putPacket + numPacketBytes, qPrintable(clientIdentifier), clientIdentifier.toLocal8Bit().size()); + numPacketBytes += clientIdentifier.toLocal8Bit().size(); + putPacket[numPacketBytes++] = '\0'; + + // pack a 1 to designate that we are putting a single value + putPacket[numPacketBytes++] = 1; + + // pack the key, null terminated + strcpy((char*) putPacket + numPacketBytes, qPrintable(key)); + numPacketBytes += key.size(); + putPacket[numPacketBytes++] = '\0'; + + // pack the value, null terminated + strcpy((char*) putPacket + numPacketBytes, qPrintable(value)); + numPacketBytes += value.size(); + putPacket[numPacketBytes++] = '\0'; + + // add the putPacket to our vector of unconfirmed packets, will be deleted once put is confirmed + _unmatchedPackets.insert(_sequenceNumber, QByteArray((char*) putPacket, numPacketBytes)); + + // send this put request to the data server + NodeList::getInstance()->getNodeSocket().writeDatagram((char*) putPacket, numPacketBytes, + dataServerSockAddr().getAddress(), + dataServerSockAddr().getPort()); + + // push the sequence number forwards + _sequenceNumber++; +} - // pack a 1 to designate that we are putting a single value - putPacket[numPacketBytes++] = 1; - - // pack the key, null terminated - strcpy((char*) putPacket + numPacketBytes, key.toLocal8Bit().constData()); - numPacketBytes += key.size(); - putPacket[numPacketBytes++] = '\0'; - - // pack the value, null terminated - strcpy((char*) putPacket + numPacketBytes, value); - numPacketBytes += strlen(value); - putPacket[numPacketBytes++] = '\0'; - - // add the putPacket to our vector of unconfirmed packets, will be deleted once put is confirmed - _unmatchedPackets.insert(_sequenceNumber, QByteArray((char*) putPacket, numPacketBytes)); - - // send this put request to the data server - NodeList::getInstance()->getNodeSocket().writeDatagram((char*) putPacket, numPacketBytes, - dataServerSockAddr().getAddress(), - dataServerSockAddr().getPort()); - - // push the sequence number forwards - _sequenceNumber++; - } +void DataServerClient::putValueForKeyAndUUID(const QString& key, const QString& value, const QUuid& uuid) { + putValueForKeyAndUsername(key, value, uuidStringWithoutCurlyBraces(uuid)); } void DataServerClient::getValueForKeyAndUUID(const QString& key, const QUuid &uuid, DataServerCallbackObject* callbackObject) { @@ -79,13 +79,13 @@ void DataServerClient::getValueForKeyAndUUID(const QString& key, const QUuid &uu void DataServerClient::getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, DataServerCallbackObject* callbackObject) { if (!uuid.isNull()) { - getValuesForKeysAndUserString(keys, uuidStringWithoutCurlyBraces(uuid), callbackObject); + getValuesForKeysAndUsername(keys, uuidStringWithoutCurlyBraces(uuid), callbackObject); } } -void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, const QString& userString, - DataServerCallbackObject* callbackObject) { - if (!userString.isEmpty() && keys.size() <= UCHAR_MAX) { +void DataServerClient::getValuesForKeysAndUsername(const QStringList& keys, const QString& clientIdentifier, + DataServerCallbackObject* callbackObject) { + if (!clientIdentifier.isEmpty() && keys.size() <= UCHAR_MAX) { unsigned char getPacket[MAX_PACKET_SIZE]; // setup the header for this packet @@ -96,8 +96,8 @@ void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, co numPacketBytes += sizeof(_sequenceNumber); // pack the user string (could be username or UUID string), null-terminate - memcpy(getPacket + numPacketBytes, userString.toLocal8Bit().constData(), userString.toLocal8Bit().size()); - numPacketBytes += userString.toLocal8Bit().size(); + memcpy(getPacket + numPacketBytes, clientIdentifier.toLocal8Bit().constData(), clientIdentifier.size()); + numPacketBytes += clientIdentifier.size(); getPacket[numPacketBytes++] = '\0'; // pack one byte to designate the number of keys @@ -122,12 +122,9 @@ void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, co } } -void DataServerClient::getClientValueForKey(const QString& key, DataServerCallbackObject* callbackObject) { - if (!_clientIdentifier.isEmpty()) { - getValuesForKeysAndUserString(QStringList(key), _clientIdentifier, callbackObject); - } else { - qDebug() << "There is no client identifier set for the DataServerClient."; - } +void DataServerClient::getClientValueForKey(const QString& key, const QString& clientIdentifier, + DataServerCallbackObject* callbackObject) { + getValuesForKeysAndUsername(QStringList(key), clientIdentifier, callbackObject); } void DataServerClient::processConfirmFromDataServer(unsigned char* packetData) { diff --git a/libraries/shared/src/DataServerClient.h b/libraries/shared/src/DataServerClient.h index 734d42ed59..11ceb8c42c 100644 --- a/libraries/shared/src/DataServerClient.h +++ b/libraries/shared/src/DataServerClient.h @@ -25,26 +25,25 @@ public: class DataServerClient { public: static const HifiSockAddr& dataServerSockAddr(); - static void putValueForKey(const QString& key, const char* value); - static void getClientValueForKey(const QString& key, DataServerCallbackObject* callbackObject); + static void putValueForKeyAndUsername(const QString& key, const QString& value, const QString& clientIdentifier); + static void putValueForKeyAndUUID(const QString& key, const QString& value, const QUuid& uuid); + + static void getClientValueForKey(const QString& key, const QString& _clientIdentifer, + DataServerCallbackObject* callbackObject); static void getValueForKeyAndUUID(const QString& key, const QUuid& uuid, DataServerCallbackObject* callbackObject); static void getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, DataServerCallbackObject* callbackObject); - static void getValuesForKeysAndUserString(const QStringList& keys, const QString& userString, - DataServerCallbackObject* callbackObject); + static void getValuesForKeysAndUsername(const QStringList& keys, const QString& clientIdentifier, + DataServerCallbackObject* callbackObject); static void processMessageFromDataServer(unsigned char* packetData, int numPacketBytes); static void resendUnmatchedPackets(); - - static void setClientIdentifier(const QString& clientIdentifier) { _clientIdentifier = clientIdentifier; } private: - static void processConfirmFromDataServer(unsigned char* packetData); static void processSendFromDataServer(unsigned char* packetData, int numPacketBytes); static void removeMatchedPacketFromMap(unsigned char* packetData); - static QString _clientIdentifier; static QMap _unmatchedPackets; static QMap _callbackObjects; static quint8 _sequenceNumber; From 1423f91717394720364d3c89b0e0e808e3c9e50e Mon Sep 17 00:00:00 2001 From: Lucas Crisman Date: Tue, 21 Jan 2014 15:48:44 -0300 Subject: [PATCH 50/58] Allow domain-server to read configuration from file --- domain-server/src/DomainServer.cpp | 92 +++++++++++++++++++++++++++--- domain-server/src/DomainServer.h | 3 + 2 files changed, 86 insertions(+), 9 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 28edf6b6a1..3e60d1509c 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -22,6 +23,10 @@ const int RESTART_HOLD_TIME_MSECS = 5 * 1000; +const char* VOXEL_SERVER_CONFIG = "voxelServerConfig"; +const char* PARTICLE_SERVER_CONFIG = "particleServerConfig"; +const char* METAVOXEL_SERVER_CONFIG = "metavoxelServerConfig"; + void signalhandler(int sig){ if (sig == SIGINT) { qApp->quit(); @@ -47,17 +52,22 @@ DomainServer::DomainServer(int argc, char* argv[]) : const char* customPortString = getCmdOption(argc, (const char**) argv, CUSTOM_PORT_OPTION); unsigned short domainServerPort = customPortString ? atoi(customPortString) : DEFAULT_DOMAIN_SERVER_PORT; + const char CONFIG_FILE_OPTION[] = "-c"; + const char* configFilePath = getCmdOption(argc, (const char**) argv, CONFIG_FILE_OPTION); + + if (!readConfigFile(configFilePath)) { + QByteArray voxelConfigOption = QString("--%1").arg(VOXEL_SERVER_CONFIG).toLocal8Bit(); + _voxelServerConfig = getCmdOption(argc, (const char**) argv, voxelConfigOption.constData()); + + QByteArray particleConfigOption = QString("--%1").arg(PARTICLE_SERVER_CONFIG).toLocal8Bit(); + _particleServerConfig = getCmdOption(argc, (const char**) argv, particleConfigOption.constData()); + + QByteArray metavoxelConfigOption = QString("--%1").arg(METAVOXEL_SERVER_CONFIG).toLocal8Bit(); + _metavoxelServerConfig = getCmdOption(argc, (const char**) argv, metavoxelConfigOption.constData()); + } + NodeList* nodeList = NodeList::createInstance(NODE_TYPE_DOMAIN, domainServerPort); - 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); - - const char METAVOXEL_CONFIG_OPTION[] = "--metavoxelServerConfig"; - _metavoxelServerConfig = getCmdOption(argc, (const char**)argv, METAVOXEL_CONFIG_OPTION); - connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), this, SLOT(nodeKilled(SharedNodePointer))); if (!_staticAssignmentFile.exists() || _voxelServerConfig) { @@ -275,6 +285,70 @@ QJsonObject jsonObjectForNode(Node* node) { return nodeJson; } +// Attempts to read configuration from specified path +// returns true on success, false otherwise +bool DomainServer::readConfigFile(const char* path) { + if (!path) { + // config file not specified + return false; + } + + if (!QFile::exists(path)) { + qWarning("Specified configuration file does not exist!\n"); + return false; + } + + QFile configFile(path); + if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning("Can't open specified configuration file!\n"); + return false; + } else { + qDebug("Reading configuration from %s\n", path); + } + QTextStream configStream(&configFile); + QByteArray configStringByteArray = configStream.readAll().toUtf8(); + QJsonObject configDocObject = QJsonDocument::fromJson(configStringByteArray).object(); + configFile.close(); + + QString voxelServerConfig = readServerAssignmentConfig(configDocObject, VOXEL_SERVER_CONFIG); + _voxelServerConfig = new char[strlen(voxelServerConfig.toLocal8Bit().constData()) +1]; + _voxelServerConfig = strcpy((char *) _voxelServerConfig, voxelServerConfig.toLocal8Bit().constData() + '\0'); + + QString particleServerConfig = readServerAssignmentConfig(configDocObject, PARTICLE_SERVER_CONFIG); + _particleServerConfig = new char[strlen(particleServerConfig.toLocal8Bit().constData()) +1]; + _particleServerConfig = strcpy((char *) _particleServerConfig, particleServerConfig.toLocal8Bit().constData() + '\0'); + + QString metavoxelServerConfig = readServerAssignmentConfig(configDocObject, METAVOXEL_SERVER_CONFIG); + _metavoxelServerConfig = new char[strlen(metavoxelServerConfig.toLocal8Bit().constData()) +1]; + _metavoxelServerConfig = strcpy((char *) _metavoxelServerConfig, metavoxelServerConfig.toLocal8Bit().constData() + '\0'); + + return true; +} + +// find assignment configurations on the specified node name and json object +// returns a string in the form of its equivalent cmd line params +QString DomainServer::readServerAssignmentConfig(QJsonObject jsonObject, const char* nodeName) { + QJsonArray nodeArray = jsonObject[nodeName].toArray(); + + QStringList serverConfig; + foreach (const QJsonValue & childValue, nodeArray) { + QString cmdParams; + QJsonObject childObject = childValue.toObject(); + QStringList keys = childObject.keys(); + for (int i = 0; i < keys.size(); i++) { + QString key = keys[i]; + QString value = childObject[key].toString(); + // both cmd line params and json keys are the same + cmdParams += QString("--%1 %2 ").arg(key, value); + } + serverConfig << cmdParams; + } + + // according to split() calls from DomainServer::prepopulateStaticAssignmentFile + // we shold simply join them with semicolons + return serverConfig.join(';'); +} + bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& path) { const QString JSON_MIME_TYPE = "application/json"; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index c9c4f0e2d7..fed5aaaa43 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -35,6 +35,9 @@ public slots: void nodeKilled(SharedNodePointer node); private: + bool readConfigFile(const char* path); + QString readServerAssignmentConfig(QJsonObject jsonObj, const char* nodeName); + void prepopulateStaticAssignmentFile(); Assignment* matchingStaticAssignmentForCheckIn(const QUuid& checkInUUID, NODE_TYPE nodeType); Assignment* deployableAssignmentForRequest(Assignment& requestAssignment); From e08f4d2eba25c2e52a1e69f4f5d6265fa54712cc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Jan 2014 13:13:57 -0800 Subject: [PATCH 51/58] more DataServerClient API tweaks --- interface/src/Application.cpp | 4 ++-- interface/src/Menu.cpp | 14 ++++++------- interface/src/avatar/Profile.cpp | 16 +++++++-------- libraries/shared/src/DataServerClient.cpp | 24 +++++++++++------------ libraries/shared/src/DataServerClient.h | 10 +++++----- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8b0fcc9b09..ac243bfa3e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1854,8 +1854,8 @@ void Application::init() { if (!_profile.getUsername().isEmpty()) { // we have a username for this avatar, ask the data-server for the mesh URL for this avatar - DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL, _profile.getUsername(), &_profile); - DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL, _profile.getUsername(), &_profile); + DataServerClient::getValueForKeyAndUserString(DataServerKey::FaceMeshURL, _profile.getUserString(), &_profile); + DataServerClient::getValueForKeyAndUserString(DataServerKey::SkeletonURL, _profile.getUserString(), &_profile); } // Set up VoxelSystem after loading preferences so we can get the desired max voxel count diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index a4fcd975ca..3e70426d09 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -830,9 +830,9 @@ void Menu::editPreferences() { applicationInstance->getProfile()->setFaceModelURL(faceModelURL); // send the new face mesh URL to the data-server (if we have a client UUID) - DataServerClient::putValueForKeyAndUsername(DataServerKey::FaceMeshURL, - faceModelURL.toString().toLocal8Bit().constData(), - applicationInstance->getProfile()->getUsername()); + DataServerClient::putValueForKeyAndUserString(DataServerKey::FaceMeshURL, + faceModelURL.toString().toLocal8Bit().constData(), + applicationInstance->getProfile()->getUserString()); } QUrl skeletonModelURL(skeletonURLEdit->text()); @@ -842,9 +842,9 @@ void Menu::editPreferences() { applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL); // send the new skeleton model URL to the data-server (if we have a client UUID) - DataServerClient::putValueForKeyAndUsername(DataServerKey::SkeletonURL, - skeletonModelURL.toString().toLocal8Bit().constData(), - applicationInstance->getProfile()->getUsername()); + DataServerClient::putValueForKeyAndUserString(DataServerKey::SkeletonURL, + skeletonModelURL.toString().toLocal8Bit().constData(), + applicationInstance->getProfile()->getUserString()); } applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum()); @@ -963,7 +963,7 @@ void Menu::goToUser() { int dialogReturn = userDialog.exec(); if (dialogReturn == QDialog::Accepted && !userDialog.textValue().isEmpty()) { // there's a username entered by the user, make a request to the data-server - DataServerClient::getValuesForKeysAndUsername( + DataServerClient::getValuesForKeysAndUserString( QStringList() << DataServerKey::Domain << DataServerKey::Position << DataServerKey::Orientation, userDialog.textValue(), Application::getInstance()->getProfile()); } diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index f701505e73..82427babc0 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -27,9 +27,9 @@ Profile::Profile(const QString &username) : setUsername(username); // we've been given a new username, ask the data-server for profile - DataServerClient::getClientValueForKey(DataServerKey::UUID, username, this); - DataServerClient::getClientValueForKey(DataServerKey::FaceMeshURL, username, this); - DataServerClient::getClientValueForKey(DataServerKey::SkeletonURL, username, this); + DataServerClient::getValueForKeyAndUserString(DataServerKey::UUID, getUserString(), this); + DataServerClient::getValueForKeyAndUserString(DataServerKey::FaceMeshURL, getUserString(), this); + DataServerClient::getValueForKeyAndUserString(DataServerKey::SkeletonURL, getUserString(), this); // send our current domain server to the data-server updateDomain(NodeList::getInstance()->getDomainHostname()); @@ -70,7 +70,7 @@ void Profile::updateDomain(const QString& domain) { _lastDomain = domain; // send the changed domain to the data-server - DataServerClient::putValueForKeyAndUsername(DataServerKey::Domain, domain, _username); + DataServerClient::putValueForKeyAndUserString(DataServerKey::Domain, domain, getUserString()); } } @@ -99,8 +99,8 @@ void Profile::updatePosition(const glm::vec3 position) { gettimeofday(&lastPositionSend, NULL); // send the changed position to the data-server - DataServerClient::putValueForKeyAndUsername(DataServerKey::Position, - QString(createByteArray(position)), _username); + DataServerClient::putValueForKeyAndUserString(DataServerKey::Position, + QString(createByteArray(position)), getUserString()); } } } @@ -116,8 +116,8 @@ void Profile::updateOrientation(const glm::quat& orientation) { uint64_t now = usecTimestampNow(); if (now - _lastOrientationSend >= DATA_SERVER_ORIENTATION_UPDATE_INTERVAL_USECS && glm::distance(_lastOrientation, eulerAngles) >= DATA_SERVER_ORIENTATION_CHANGE_THRESHOLD_DEGREES) { - DataServerClient::putValueForKeyAndUsername(DataServerKey::Orientation, QString(createByteArray(eulerAngles)), - _username); + DataServerClient::putValueForKeyAndUserString(DataServerKey::Orientation, QString(createByteArray(eulerAngles)), + getUserString()); _lastOrientation = eulerAngles; _lastOrientationSend = now; diff --git a/libraries/shared/src/DataServerClient.cpp b/libraries/shared/src/DataServerClient.cpp index b16739c9bf..2eb8fcc1f0 100644 --- a/libraries/shared/src/DataServerClient.cpp +++ b/libraries/shared/src/DataServerClient.cpp @@ -29,7 +29,7 @@ const HifiSockAddr& DataServerClient::dataServerSockAddr() { return dsSockAddr; } -void DataServerClient::putValueForKeyAndUsername(const QString& key, const QString& value, const QString& clientIdentifier) { +void DataServerClient::putValueForKeyAndUserString(const QString& key, const QString& value, const QString& userString) { unsigned char putPacket[MAX_PACKET_SIZE]; // setup the header for this packet @@ -40,8 +40,8 @@ void DataServerClient::putValueForKeyAndUsername(const QString& key, const QStri numPacketBytes += sizeof(_sequenceNumber); // pack the client UUID, null terminated - memcpy(putPacket + numPacketBytes, qPrintable(clientIdentifier), clientIdentifier.toLocal8Bit().size()); - numPacketBytes += clientIdentifier.toLocal8Bit().size(); + memcpy(putPacket + numPacketBytes, qPrintable(userString), userString.size()); + numPacketBytes += userString.size(); putPacket[numPacketBytes++] = '\0'; // pack a 1 to designate that we are putting a single value @@ -70,7 +70,7 @@ void DataServerClient::putValueForKeyAndUsername(const QString& key, const QStri } void DataServerClient::putValueForKeyAndUUID(const QString& key, const QString& value, const QUuid& uuid) { - putValueForKeyAndUsername(key, value, uuidStringWithoutCurlyBraces(uuid)); + putValueForKeyAndUserString(key, value, uuidStringWithoutCurlyBraces(uuid)); } void DataServerClient::getValueForKeyAndUUID(const QString& key, const QUuid &uuid, DataServerCallbackObject* callbackObject) { @@ -79,13 +79,13 @@ void DataServerClient::getValueForKeyAndUUID(const QString& key, const QUuid &uu void DataServerClient::getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, DataServerCallbackObject* callbackObject) { if (!uuid.isNull()) { - getValuesForKeysAndUsername(keys, uuidStringWithoutCurlyBraces(uuid), callbackObject); + getValuesForKeysAndUserString(keys, uuidStringWithoutCurlyBraces(uuid), callbackObject); } } -void DataServerClient::getValuesForKeysAndUsername(const QStringList& keys, const QString& clientIdentifier, +void DataServerClient::getValuesForKeysAndUserString(const QStringList& keys, const QString& userString, DataServerCallbackObject* callbackObject) { - if (!clientIdentifier.isEmpty() && keys.size() <= UCHAR_MAX) { + if (!userString.isEmpty() && keys.size() <= UCHAR_MAX) { unsigned char getPacket[MAX_PACKET_SIZE]; // setup the header for this packet @@ -96,8 +96,8 @@ void DataServerClient::getValuesForKeysAndUsername(const QStringList& keys, cons numPacketBytes += sizeof(_sequenceNumber); // pack the user string (could be username or UUID string), null-terminate - memcpy(getPacket + numPacketBytes, clientIdentifier.toLocal8Bit().constData(), clientIdentifier.size()); - numPacketBytes += clientIdentifier.size(); + memcpy(getPacket + numPacketBytes, qPrintable(userString), userString.size()); + numPacketBytes += userString.size(); getPacket[numPacketBytes++] = '\0'; // pack one byte to designate the number of keys @@ -122,9 +122,9 @@ void DataServerClient::getValuesForKeysAndUsername(const QStringList& keys, cons } } -void DataServerClient::getClientValueForKey(const QString& key, const QString& clientIdentifier, - DataServerCallbackObject* callbackObject) { - getValuesForKeysAndUsername(QStringList(key), clientIdentifier, callbackObject); +void DataServerClient::getValueForKeyAndUserString(const QString& key, const QString& userString, + DataServerCallbackObject* callbackObject) { + getValuesForKeysAndUserString(QStringList(key), userString, callbackObject); } void DataServerClient::processConfirmFromDataServer(unsigned char* packetData) { diff --git a/libraries/shared/src/DataServerClient.h b/libraries/shared/src/DataServerClient.h index 11ceb8c42c..0ce9355f4b 100644 --- a/libraries/shared/src/DataServerClient.h +++ b/libraries/shared/src/DataServerClient.h @@ -26,15 +26,15 @@ class DataServerClient { public: static const HifiSockAddr& dataServerSockAddr(); - static void putValueForKeyAndUsername(const QString& key, const QString& value, const QString& clientIdentifier); + static void putValueForKeyAndUserString(const QString& key, const QString& value, const QString& userString); static void putValueForKeyAndUUID(const QString& key, const QString& value, const QUuid& uuid); - static void getClientValueForKey(const QString& key, const QString& _clientIdentifer, - DataServerCallbackObject* callbackObject); + static void getValueForKeyAndUserString(const QString& key, const QString& userString, + DataServerCallbackObject* callbackObject); static void getValueForKeyAndUUID(const QString& key, const QUuid& uuid, DataServerCallbackObject* callbackObject); static void getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, DataServerCallbackObject* callbackObject); - static void getValuesForKeysAndUsername(const QStringList& keys, const QString& clientIdentifier, - DataServerCallbackObject* callbackObject); + static void getValuesForKeysAndUserString(const QStringList& keys, const QString& userString, + DataServerCallbackObject* callbackObject); static void processMessageFromDataServer(unsigned char* packetData, int numPacketBytes); From aa9f7ff31db9111b2647a95e8abaf68ef42da2cf Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Jan 2014 13:21:03 -0800 Subject: [PATCH 52/58] hook the DataServerScriptingInterface to the ScriptEngine --- libraries/script-engine/src/DataServerScriptingInterface.h | 2 ++ libraries/script-engine/src/ScriptEngine.cpp | 6 ++++++ libraries/script-engine/src/ScriptEngine.h | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/libraries/script-engine/src/DataServerScriptingInterface.h b/libraries/script-engine/src/DataServerScriptingInterface.h index 1cba837b2c..4a50bc087a 100644 --- a/libraries/script-engine/src/DataServerScriptingInterface.h +++ b/libraries/script-engine/src/DataServerScriptingInterface.h @@ -17,6 +17,8 @@ class DataServerScriptingInterface : public QObject { Q_OBJECT public: DataServerScriptingInterface(); + + void refreshUUID() { _uuid = QUuid::createUuid(); } public slots: void setValueForKey(const QString& key, const QString& value); private: diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 6b85cf33ad..2c08bd9b08 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -119,6 +119,8 @@ void ScriptEngine::init() { QScriptValue audioScriptingInterfaceValue = _engine.newQObject(&_audioScriptingInterface); _engine.globalObject().setProperty("Audio", audioScriptingInterfaceValue); + + registerGlobalObject("Data", &_dataServerScriptingInterface); if (_controllerScriptingInterface) { QScriptValue controllerScripterValue = _engine.newQObject(_controllerScriptingInterface); @@ -140,6 +142,10 @@ void ScriptEngine::registerGlobalObject(const QString& name, QObject* object) { _engine.globalObject().setProperty(name, value); } +void ScriptEngine::preEvaluateReset() { + _dataServerScriptingInterface.refreshUUID(); +} + void ScriptEngine::evaluate() { if (!_isInitialized) { init(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 06597f82a8..ab38b48bbf 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -22,6 +22,7 @@ class ParticlesScriptingInterface; #include "AbstractControllerScriptingInterface.h" +#include "DataServerScriptingInterface.h" const QString NO_SCRIPT(""); @@ -61,6 +62,8 @@ signals: void finished(); protected: + void preEvaluateReset(); + QString _scriptContents; bool _isFinished; bool _isRunning; @@ -72,6 +75,7 @@ private: static ParticlesScriptingInterface _particlesScriptingInterface; AbstractControllerScriptingInterface* _controllerScriptingInterface; AudioScriptingInterface _audioScriptingInterface; + DataServerScriptingInterface _dataServerScriptingInterface; bool _wantMenuItems; QString _scriptMenuName; AbstractMenuInterface* _menu; From 417aa7ad59db9187a2b15d2ea0460539eef2d1ff Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Jan 2014 15:59:55 -0800 Subject: [PATCH 53/58] re-expose an Avatar to the ScriptEngine --- assignment-client/src/Agent.cpp | 11 +++++- .../src/DataServerScriptingInterface.h | 2 ++ libraries/script-engine/src/ScriptEngine.cpp | 35 ++++++++++++++++++- libraries/script-engine/src/ScriptEngine.h | 14 ++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index ca89d3998f..d27845f337 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -56,7 +56,7 @@ void Agent::run() { // other node types, but for them to get access to those node types, we have to add them here. It seems like // NodeList should support adding types of interest const NODE_TYPE AGENT_NODE_TYPES_OF_INTEREST[] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_PARTICLE_SERVER, - NODE_TYPE_AUDIO_MIXER }; + NODE_TYPE_AUDIO_MIXER, NODE_TYPE_AVATAR_MIXER }; nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST)); @@ -93,6 +93,15 @@ void Agent::run() { QTimer* pingNodesTimer = new QTimer(this); connect(pingNodesTimer, SIGNAL(timeout()), nodeList, SLOT(pingInactiveNodes())); pingNodesTimer->start(PING_INACTIVE_NODE_INTERVAL_USECS / 1000); + + // setup an Avatar for the script to use + AvatarData scriptedAvatar; + + // match the scripted avatar's UUID to the DataServerScriptingInterface UUID + scriptedAvatar.setUUID(_scriptEngine.getDataServerScriptingInterface().getUUID()); + + // give this AvatarData object to the script engine + _scriptEngine.setAvatarData(&scriptedAvatar); _scriptEngine.setScriptContents(scriptContents); _scriptEngine.run(); diff --git a/libraries/script-engine/src/DataServerScriptingInterface.h b/libraries/script-engine/src/DataServerScriptingInterface.h index 4a50bc087a..8bd3eea565 100644 --- a/libraries/script-engine/src/DataServerScriptingInterface.h +++ b/libraries/script-engine/src/DataServerScriptingInterface.h @@ -19,6 +19,8 @@ public: DataServerScriptingInterface(); void refreshUUID() { _uuid = QUuid::createUuid(); } + const QUuid& getUUID() const { return _uuid; } + public slots: void setValueForKey(const QString& key, const QString& value); private: diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 2c08bd9b08..1b2f121b5b 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -40,7 +40,9 @@ static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* eng ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, const char* scriptMenuName, AbstractMenuInterface* menu, - AbstractControllerScriptingInterface* controllerScriptingInterface) { + AbstractControllerScriptingInterface* controllerScriptingInterface) : + _avatarData(NULL) +{ _scriptContents = scriptContents; _isFinished = false; _isRunning = false; @@ -65,6 +67,15 @@ ScriptEngine::~ScriptEngine() { //printf("ScriptEngine::~ScriptEngine()...\n"); } +void ScriptEngine::setAvatarData(AvatarData* avatarData) { + _avatarData = avatarData; + + // remove the old Avatar property, if it exists + _engine.globalObject().setProperty("Avatar", QScriptValue()); + + // give the script engine the new Avatar script property + registerGlobalObject("Avatar", _avatarData); +} void ScriptEngine::setupMenuItems() { if (_menu && _wantMenuItems) { @@ -178,6 +189,8 @@ void ScriptEngine::run() { gettimeofday(&startTime, NULL); int thisFrame = 0; + + NodeList* nodeList = NodeList::getInstance(); while (!_isFinished) { int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow(); @@ -221,6 +234,26 @@ void ScriptEngine::run() { _particlesScriptingInterface.getParticlePacketSender()->process(); } } + + if (_isAvatar && _avatarData) { + static unsigned char avatarPacket[MAX_PACKET_SIZE]; + static int numAvatarHeaderBytes = 0; + + if (numAvatarHeaderBytes == 0) { + // pack the avatar header bytes the first time + // unlike the _avatar.getBroadcastData these won't change + numAvatarHeaderBytes = populateTypeAndVersion(avatarPacket, PACKET_TYPE_HEAD_DATA); + + // pack the owner UUID for this script + QByteArray ownerUUID = nodeList->getOwnerUUID().toRfc4122(); + memcpy(avatarPacket, ownerUUID.constData(), ownerUUID.size()); + numAvatarHeaderBytes += ownerUUID.size(); + } + + int numAvatarPacketBytes = _avatarData->getBroadcastData(avatarPacket + numAvatarHeaderBytes) + numAvatarHeaderBytes; + + nodeList->broadcastToNodes(avatarPacket, numAvatarPacketBytes, &NODE_TYPE_AVATAR_MIXER, 1); + } if (willSendVisualDataCallBack) { emit willSendVisualDataCallback(); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index ab38b48bbf..9f1fbeacae 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -19,6 +19,8 @@ #include #include +#include + class ParticlesScriptingInterface; #include "AbstractControllerScriptingInterface.h" @@ -28,6 +30,8 @@ const QString NO_SCRIPT(""); class ScriptEngine : public QObject { Q_OBJECT + + Q_PROPERTY(bool isAvatar READ isAvatar WRITE setIsAvatar) public: ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false, const char* scriptMenuName = NULL, AbstractMenuInterface* menu = NULL, @@ -40,6 +44,9 @@ public: /// Access the ParticlesScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener ParticlesScriptingInterface* getParticlesScriptingInterface() { return &_particlesScriptingInterface; } + + /// Access the DataServerScriptingInterface for access to its underlying UUID + const DataServerScriptingInterface& getDataServerScriptingInterface() { return _dataServerScriptingInterface; } /// sets the script contents, will return false if failed, will fail if script is already running bool setScriptContents(const QString& scriptContents); @@ -48,6 +55,11 @@ public: void cleanMenuItems(); void registerGlobalObject(const QString& name, QObject* object); /// registers a global object by name + + void setIsAvatar(bool isAvatar) { _isAvatar = isAvatar; } + bool isAvatar() const { return _isAvatar; } + + void setAvatarData(AvatarData* avatarData); public slots: void init(); @@ -69,6 +81,7 @@ protected: bool _isRunning; bool _isInitialized; QScriptEngine _engine; + bool _isAvatar; private: static VoxelsScriptingInterface _voxelsScriptingInterface; @@ -76,6 +89,7 @@ private: AbstractControllerScriptingInterface* _controllerScriptingInterface; AudioScriptingInterface _audioScriptingInterface; DataServerScriptingInterface _dataServerScriptingInterface; + AvatarData* _avatarData; bool _wantMenuItems; QString _scriptMenuName; AbstractMenuInterface* _menu; From 8e5d4b69f47412650410bf6168cf2316352a448a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Jan 2014 16:14:33 -0800 Subject: [PATCH 54/58] fix packet offset for owner UUID --- libraries/script-engine/src/ScriptEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 7d0d68c0c6..c56bb1ada7 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -250,7 +250,7 @@ void ScriptEngine::run() { // pack the owner UUID for this script QByteArray ownerUUID = nodeList->getOwnerUUID().toRfc4122(); - memcpy(avatarPacket, ownerUUID.constData(), ownerUUID.size()); + memcpy(avatarPacket + numAvatarHeaderBytes, ownerUUID.constData(), ownerUUID.size()); numAvatarHeaderBytes += ownerUUID.size(); } From 105fb42b340b358452b14fbccab6132f28dc9002 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Jan 2014 16:46:04 -0800 Subject: [PATCH 55/58] rewrite mesh and skeleton requests from data-server --- interface/src/Application.cpp | 56 ++--------------------- interface/src/Application.h | 1 - interface/src/avatar/Avatar.cpp | 27 +++++------ interface/src/avatar/Avatar.h | 1 - libraries/shared/src/DataServerClient.cpp | 19 +++++--- libraries/shared/src/PacketHeaders.cpp | 3 -- libraries/shared/src/PacketHeaders.h | 1 - 7 files changed, 27 insertions(+), 81 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5c943f5945..8e1ba5afa1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1324,6 +1324,9 @@ void Application::timer() { // ask the node list to check in with the domain server NodeList::getInstance()->sendDomainServerCheckIn(); + + // send unmatched DataServerClient packets + DataServerClient::resendUnmatchedPackets(); // give the MyAvatar object position, orientation to the Profile so it can propagate to the data-server _profile.updatePosition(_myAvatar.getPosition()); @@ -1416,51 +1419,6 @@ void Application::terminate() { } } -static Avatar* processAvatarMessageHeader(unsigned char*& packetData, size_t& dataBytes) { - // record the packet for stats-tracking - Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::AVATARS).updateValue(dataBytes); - SharedNodePointer avatarMixerNode = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AVATAR_MIXER); - if (avatarMixerNode) { - avatarMixerNode->recordBytesReceived(dataBytes); - } - - // skip the header - int numBytesPacketHeader = numBytesForPacketHeader(packetData); - packetData += numBytesPacketHeader; - dataBytes -= numBytesPacketHeader; - - // read the node id - QUuid nodeUUID = QUuid::fromRfc4122(QByteArray((char*) packetData, NUM_BYTES_RFC4122_UUID)); - - packetData += NUM_BYTES_RFC4122_UUID; - dataBytes -= NUM_BYTES_RFC4122_UUID; - - // make sure the node exists - SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(nodeUUID); - if (!node || !node->getLinkedData()) { - return NULL; - } - Avatar* avatar = static_cast(node->getLinkedData()); - return avatar->isInitialized() ? avatar : NULL; -} - -void Application::processAvatarURLsMessage(unsigned char* packetData, size_t dataBytes) { - Avatar* avatar = processAvatarMessageHeader(packetData, dataBytes); - if (!avatar) { - return; - } - // PER Note: message is no longer processed but used to trigger - // Dataserver lookup - redesign this to instantly ask the - // dataserver on first receipt of other avatar UUID, and also - // don't ask over and over again. Instead use this message to - // Tell the other avatars that your dataserver data has - // changed. - - // use this timing to as the data-server for an updated mesh for this avatar (if we have UUID) - DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL, - avatar->getUUID(), &_profile); -} - void Application::checkBandwidthMeterClick() { // ... to be called upon button release @@ -2554,11 +2512,6 @@ void Application::updateAvatar(float deltaTime) { controlledBroadcastToNodes(broadcastString, endOfBroadcastStringWrite - broadcastString, nodeTypesOfInterest, sizeof(nodeTypesOfInterest)); - const float AVATAR_URLS_SEND_INTERVAL = 1.0f; - if (shouldDo(AVATAR_URLS_SEND_INTERVAL, deltaTime)) { - QUrl empty; - Avatar::sendAvatarURLsMessage(empty); - } // Update _viewFrustum with latest camera and view frustum data... // NOTE: we get this from the view frustum, to make it simpler, since the // loadViewFrumstum() method will get the correct details from the camera @@ -4225,9 +4178,6 @@ void Application::processDatagrams() { bytesReceived); getInstance()->_bandwidthMeter.inputStream(BandwidthMeter::AVATARS).updateValue(bytesReceived); break; - case PACKET_TYPE_AVATAR_URLS: - processAvatarURLsMessage(_incomingPacket, bytesReceived); - break; case PACKET_TYPE_DATA_SERVER_GET: case PACKET_TYPE_DATA_SERVER_PUT: case PACKET_TYPE_DATA_SERVER_SEND: diff --git a/interface/src/Application.h b/interface/src/Application.h index 242b75c0ba..63a3209a9f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -267,7 +267,6 @@ private: void updateProjectionMatrix(Camera& camera, bool updateViewFrustum = true); static bool sendVoxelsOperation(OctreeElement* node, void* extraData); - void processAvatarURLsMessage(unsigned char* packetData, size_t dataBytes); static void sendPingPackets(); void initDisplay(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 6db0a15300..30de5b1e2a 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -58,21 +58,6 @@ const int NUM_BODY_CONE_SIDES = 9; const float CHAT_MESSAGE_SCALE = 0.0015f; const float CHAT_MESSAGE_HEIGHT = 0.1f; -void Avatar::sendAvatarURLsMessage(const QUrl& voxelURL) { - QByteArray message; - - char packetHeader[MAX_PACKET_HEADER_BYTES]; - int numBytesPacketHeader = populateTypeAndVersion((unsigned char*) packetHeader, PACKET_TYPE_AVATAR_URLS); - - message.append(packetHeader, numBytesPacketHeader); - message.append(NodeList::getInstance()->getOwnerUUID().toRfc4122()); - - QDataStream out(&message, QIODevice::WriteOnly | QIODevice::Append); - out << voxelURL; - - Application::controlledBroadcastToNodes((unsigned char*)message.data(), message.size(), &NODE_TYPE_AVATAR_MIXER, 1); -} - Avatar::Avatar(Node* owningNode) : AvatarData(owningNode), _head(this), @@ -359,9 +344,21 @@ bool Avatar::findSphereCollision(const glm::vec3& sphereCenter, float sphereRadi int Avatar::parseData(unsigned char* sourceBuffer, int numBytes) { // change in position implies movement glm::vec3 oldPosition = _position; + + // change in UUID requires mesh and skeleton request to data-server + + QUuid oldUuid = _uuid; + int bytesRead = AvatarData::parseData(sourceBuffer, numBytes); + const float MOVE_DISTANCE_THRESHOLD = 0.001f; _moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD; + + if (oldUuid != _uuid && !_uuid.isNull()) { + DataServerClient::getValuesForKeysAndUUID(QStringList() << DataServerKey::FaceMeshURL << DataServerKey::SkeletonURL, + _uuid, Application::getInstance()->getProfile()); + } + return bytesRead; } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 3f1da6bc31..725aa9d5cf 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -69,7 +69,6 @@ class Avatar : public AvatarData { Q_OBJECT public: - static void sendAvatarURLsMessage(const QUrl& voxelURL); Avatar(Node* owningNode = NULL); ~Avatar(); diff --git a/libraries/shared/src/DataServerClient.cpp b/libraries/shared/src/DataServerClient.cpp index 2eb8fcc1f0..a7a213f42c 100644 --- a/libraries/shared/src/DataServerClient.cpp +++ b/libraries/shared/src/DataServerClient.cpp @@ -77,7 +77,8 @@ void DataServerClient::getValueForKeyAndUUID(const QString& key, const QUuid &uu getValuesForKeysAndUUID(QStringList(key), uuid, callbackObject); } -void DataServerClient::getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, DataServerCallbackObject* callbackObject) { +void DataServerClient::getValuesForKeysAndUUID(const QStringList& keys, const QUuid& uuid, + DataServerCallbackObject* callbackObject) { if (!uuid.isNull()) { getValuesForKeysAndUserString(keys, uuidStringWithoutCurlyBraces(uuid), callbackObject); } @@ -137,7 +138,7 @@ void DataServerClient::processSendFromDataServer(unsigned char* packetData, int quint8 sequenceNumber = *(packetData + numHeaderBytes); - if (_callbackObjects.find(_sequenceNumber) != _callbackObjects.end()) { + if (_callbackObjects.find(sequenceNumber) != _callbackObjects.end()) { // remove the packet from our two maps, it's matched DataServerCallbackObject* callbackObject = _callbackObjects.take(sequenceNumber); _unmatchedPackets.remove(sequenceNumber); @@ -176,10 +177,14 @@ void DataServerClient::removeMatchedPacketFromMap(unsigned char* packetData) { } void DataServerClient::resendUnmatchedPackets() { - foreach (const QByteArray& packet, _unmatchedPackets) { - // send the unmatched packet to the data server - NodeList::getInstance()->getNodeSocket().writeDatagram(packet.data(), packet.size(), - dataServerSockAddr().getAddress(), - dataServerSockAddr().getPort()); + if (_unmatchedPackets.size() > 0) { + qDebug() << "Resending" << _unmatchedPackets.size() << "packets to the data server."; + + foreach (const QByteArray& packet, _unmatchedPackets) { + // send the unmatched packet to the data server + NodeList::getInstance()->getNodeSocket().writeDatagram(packet.data(), packet.size(), + dataServerSockAddr().getAddress(), + dataServerSockAddr().getPort()); + } } } diff --git a/libraries/shared/src/PacketHeaders.cpp b/libraries/shared/src/PacketHeaders.cpp index f2af6cd81c..83dbf4654f 100644 --- a/libraries/shared/src/PacketHeaders.cpp +++ b/libraries/shared/src/PacketHeaders.cpp @@ -21,9 +21,6 @@ PACKET_VERSION versionForPacketType(PACKET_TYPE type) { case PACKET_TYPE_HEAD_DATA: return 15; - - case PACKET_TYPE_AVATAR_URLS: - return 2; case PACKET_TYPE_OCTREE_STATS: return 2; diff --git a/libraries/shared/src/PacketHeaders.h b/libraries/shared/src/PacketHeaders.h index f4fe5cd0a8..5d1c657d6b 100644 --- a/libraries/shared/src/PacketHeaders.h +++ b/libraries/shared/src/PacketHeaders.h @@ -25,7 +25,6 @@ const PACKET_TYPE PACKET_TYPE_MIXED_AUDIO = 'A'; const PACKET_TYPE PACKET_TYPE_MICROPHONE_AUDIO_NO_ECHO = 'M'; const PACKET_TYPE PACKET_TYPE_MICROPHONE_AUDIO_WITH_ECHO = 'm'; const PACKET_TYPE PACKET_TYPE_BULK_AVATAR_DATA = 'X'; -const PACKET_TYPE PACKET_TYPE_AVATAR_URLS = 'U'; const PACKET_TYPE PACKET_TYPE_TRANSMITTER_DATA_V2 = 'T'; const PACKET_TYPE PACKET_TYPE_ENVIRONMENT_DATA = 'e'; const PACKET_TYPE PACKET_TYPE_DOMAIN_LIST_REQUEST = 'L'; From f9b38af96fd9229d23df2c424a381cc93d380bf0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Jan 2014 16:55:57 -0800 Subject: [PATCH 56/58] remove processing of Avatar URLs from AvatarMixer --- assignment-client/src/avatars/AvatarMixer.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index e5e3ea2fcc..56464d2415 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -137,19 +137,6 @@ void AvatarMixer::processDatagram(const QByteArray& dataByteArray, const HifiSoc } } case PACKET_TYPE_KILL_NODE: - case PACKET_TYPE_AVATAR_URLS: { - QUuid nodeUUID = QUuid::fromRfc4122(dataByteArray.mid(numBytesForPacketHeader((unsigned char*) dataByteArray.data()), - NUM_BYTES_RFC4122_UUID)); - // let everyone else know about the update - foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { - if (node->getActiveSocket() && node->getUUID() != nodeUUID) { - nodeList->getNodeSocket().writeDatagram(dataByteArray, - node->getActiveSocket()->getAddress(), - node->getActiveSocket()->getPort()); - } - } - // let node kills fall through to default behavior - } default: // hand this off to the NodeList nodeList->processNodeData(senderSockAddr, (unsigned char*) dataByteArray.data(), dataByteArray.size()); From 8df40b5d6014bb7643169040f551960b082abc3c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Jan 2014 17:07:40 -0800 Subject: [PATCH 57/58] clearly init the skeleton URL in profile --- interface/src/avatar/Profile.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Profile.cpp b/interface/src/avatar/Profile.cpp index 82427babc0..09baac13a9 100644 --- a/interface/src/avatar/Profile.cpp +++ b/interface/src/avatar/Profile.cpp @@ -21,7 +21,8 @@ Profile::Profile(const QString &username) : _lastDomain(), _lastPosition(0.0, 0.0, 0.0), _lastOrientationSend(0), - _faceModelURL() + _faceModelURL(), + _skeletonModelURL() { if (!username.isEmpty()) { setUsername(username); From 2651c06367164bd0f04a49b5871b2bd2bc15920d Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 21 Jan 2014 17:21:57 -0800 Subject: [PATCH 58/58] Compile fix (for me, anyway). --- libraries/shared/src/DataServerClient.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/shared/src/DataServerClient.h b/libraries/shared/src/DataServerClient.h index 0ce9355f4b..78826022ae 100644 --- a/libraries/shared/src/DataServerClient.h +++ b/libraries/shared/src/DataServerClient.h @@ -13,6 +13,7 @@ #include #include +#include #include #include "HifiSockAddr.h"