Merge remote-tracking branch 'upstream/master' into web-entity

This commit is contained in:
Brad Davis 2015-05-13 09:49:53 -07:00
commit 9b6c20d93c
31 changed files with 1348 additions and 879 deletions

View file

@ -1,7 +1,7 @@
[
{
"name": "metaverse",
"label": "Metaverse Registration",
"label": "Metaverse / Networking",
"settings": [
{
"name": "access_token",
@ -44,6 +44,29 @@
}
]
},
{
"label": "Paths",
"settings": [
{
"name": "paths",
"label": "",
"help": "Clients can enter a path to reach an exact viewpoint in your domain.<br/>Add rows to the table below to map a path to a viewpoint.<br/>The index path ( / ) is where clients will enter if they do not enter an explicit path.",
"type": "table",
"key": {
"name": "path",
"label": "Path",
"placeholder": "/"
},
"columns": [
{
"name": "viewpoint",
"label": "Viewpoint",
"placeholder": "/512,512,512"
}
]
}
]
},
{
"name": "security",
"label": "Security",

View file

@ -21,34 +21,34 @@ var Settings = {
};
var viewHelpers = {
getFormGroup: function(groupName, setting, values, isAdvanced, isLocked) {
setting_name = groupName + "." + setting.name
getFormGroup: function(keypath, setting, values, isAdvanced, isLocked) {
form_group = "<div class='form-group " + (isAdvanced ? Settings.ADVANCED_CLASS : "") + "'>";
form_group = "<div class='form-group " + (isAdvanced ? Settings.ADVANCED_CLASS : "") + "'>"
setting_value = _(values).valueForKeyPath(keypath);
if (_.has(values, groupName) && _.has(values[groupName], setting.name)) {
setting_value = values[groupName][setting.name]
} else if (_.has(setting, 'default')) {
setting_value = setting.default
if (!setting_value) {
if (_.has(setting, 'default')) {
setting_value = setting.default;
} else {
setting_value = ""
setting_value = "";
}
}
label_class = 'control-label'
label_class = 'control-label';
if (isLocked) {
label_class += ' locked'
label_class += ' locked';
}
common_attrs = " class='" + (setting.type !== 'checkbox' ? 'form-control' : '')
+ " " + Settings.TRIGGER_CHANGE_CLASS + "' data-short-name='" + setting.name + "' name='" + setting_name + "' "
+ "id='" + setting_name + "'"
+ " " + Settings.TRIGGER_CHANGE_CLASS + "' data-short-name='" + setting.name + "' name='" + keypath + "' "
+ "id='" + keypath + "'";
if (setting.type === 'checkbox') {
if (setting.label) {
form_group += "<label class='" + label_class + "'>" + setting.label + "</label>"
}
form_group += "<div class='checkbox" + (isLocked ? " disabled" : "") + "'>"
form_group += "<label for='" + setting_name + "'>"
form_group += "<label for='" + keypath + "'>"
form_group += "<input type='checkbox'" + common_attrs + (setting_value ? "checked" : "") + (isLocked ? " disabled" : "") + "/>"
form_group += " " + setting.help + "</label>";
form_group += "</div>"
@ -56,14 +56,14 @@ var viewHelpers = {
input_type = _.has(setting, 'type') ? setting.type : "text"
if (setting.label) {
form_group += "<label for='" + setting_name + "' class='" + label_class + "'>" + setting.label + "</label>";
form_group += "<label for='" + keypath + "' class='" + label_class + "'>" + setting.label + "</label>";
}
if (input_type === 'table') {
form_group += makeTable(setting, setting_name, setting_value, isLocked)
form_group += makeTable(setting, keypath, setting_value, isLocked)
} else {
if (input_type === 'select') {
form_group += "<select class='form-control' data-hidden-input='" + setting_name + "'>'"
form_group += "<select class='form-control' data-hidden-input='" + keypath + "'>'"
_.each(setting.options, function(option) {
form_group += "<option value='" + option.value + "'" +
@ -186,7 +186,7 @@ $(document).ready(function(){
// $('body').scrollspy({ target: '#setup-sidebar'})
reloadSettings()
reloadSettings();
})
function reloadSettings() {
@ -258,16 +258,21 @@ $('body').on('click', '.save-button', function(e){
return false;
});
function makeTable(setting, setting_name, setting_value, isLocked) {
function makeTable(setting, keypath, setting_value, isLocked) {
var isArray = !_.has(setting, 'key')
if (!isArray && setting.can_order) {
setting.can_order = false;
}
var html = "<span class='help-block'>" + setting.help + "</span>"
html += "<table class='table table-bordered " + (isLocked ? "locked-table" : "") + "' data-short-name='" + setting.name + "' name='" + setting_name
+ "' data-setting-type='" + (isArray ? 'array' : 'hash') + "'>"
var html = "";
if (setting.help) {
html += "<span class='help-block'>" + setting.help + "</span>"
}
html += "<table class='table table-bordered " + (isLocked ? "locked-table" : "") + "' data-short-name='" + setting.name
+ "' name='" + keypath + "' data-setting-type='" + (isArray ? 'array' : 'hash') + "'>"
// Column names
html += "<tr class='headers'>"
@ -296,7 +301,7 @@ function makeTable(setting, setting_name, setting_value, isLocked) {
var row_num = 1
_.each(setting_value, function(row, indexOrName) {
html += "<tr class='" + Settings.DATA_ROW_CLASS + "'" + (isArray ? "" : "name='" + setting_name + "." + indexOrName + "'") + ">"
html += "<tr class='" + Settings.DATA_ROW_CLASS + "'" + (isArray ? "" : "name='" + keypath + "." + indexOrName + "'") + ">"
if (setting.numbered === true) {
html += "<td class='numbered'>" + row_num + "</td>"
@ -315,7 +320,7 @@ function makeTable(setting, setting_name, setting_value, isLocked) {
html += colValue
// for arrays we add a hidden input to this td so that values can be posted appropriately
html += "<input type='hidden' name='" + setting_name + "[" + indexOrName + "]"
html += "<input type='hidden' name='" + keypath + "[" + indexOrName + "]"
+ (rowIsObject ? "." + col.name : "") + "' value='" + colValue + "'/>"
} else if (row.hasOwnProperty(col.name)) {
html += row[col.name]
@ -382,9 +387,20 @@ function badgeSidebarForDifferences(changedElement) {
// figure out which group this input is in
var panelParentID = changedElement.closest('.panel').attr('id')
// if the panel contains non-grouped settings, the initial value is Settings.initialValues
var isGrouped = $(panelParentID).hasClass('grouped');
if (isGrouped) {
var initialPanelJSON = Settings.initialValues[panelParentID];
// get a JSON representation of that section
var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID]
var initialPanelJSON = Settings.initialValues[panelParentID]
var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID];
} else {
var initialPanelJSON = Settings.initialValues;
// get a JSON representation of that section
var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true);
}
var badgeValue = 0

View file

@ -10,8 +10,9 @@
<div id="setup-sidebar" data-clampedwidth="#setup-sidebar-col" class="hidden-xs" data-spy="affix" data-offset-top="55">
<script id="list-group-template" type="text/template">
<% _.each(descriptions, function(group){ %>
<% panelID = group.name ? group.name : group.label %>
<li>
<a href="#<%-group.name %>" class="list-group-item">
<a href="#<%- panelID %>" class="list-group-item">
<span class="badge"></span>
<%- group.label %>
</a>
@ -37,24 +38,32 @@
<% if (isAdvanced) { %>
<% $("a[href=#" + group.name + "]").addClass('advanced-setting').hide() %>
<% } %>
<div class="panel panel-default <%- (isAdvanced) ? 'advanced-setting' : '' %>" id="<%- group.name %>">
<% isGrouped = !!group.name %>
<% panelID = isGrouped ? group.name : group.label %>
<div class="panel panel-default<%- (isAdvanced) ? ' advanced-setting' : '' %><%- (isGrouped) ? ' grouped' : '' %>"
id="<%- panelID %>">
<div class="panel-heading">
<h3 class="panel-title"><%- group.label %></h3>
</div>
<div class="panel-body">
<% _.each(split_settings[0], function(setting) { %>
<%= getFormGroup(group.name, setting, values, false,
<% keypath = isGrouped ? group.name + "." + setting.name : setting.name %>
<%= getFormGroup(keypath, setting, values, false,
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
<% }); %>
<% if (!_.isEmpty(split_settings[1])) { %>
<% $("#advanced-toggle-button").show() %>
<% _.each(split_settings[1], function(setting) { %>
<%= getFormGroup(group.name, setting, values, true,
<% keypath = isGrouped ? group.name + "." + setting.name : setting.name %>
<%= getFormGroup(keypath, setting, values, true,
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
<% }); %>
<% }%>
</div>
</div>
<% }); %>
</script>
@ -83,6 +92,7 @@
<!--#include virtual="footer.html"-->
<script src='/js/underscore-min.js'></script>
<script src='/js/underscore-keypath.min.js'></script>
<script src='/js/bootbox.min.js'></script>
<script src='/js/sweet-alert.min.js'></script>
<script src='/js/settings.js'></script>

View file

@ -9,6 +9,6 @@
<script src='js/json.human.js'></script>
<script src='js/highcharts-custom.js'></script>
<script src='/js/underscore-min.js'></script>
<script src='js/underscore-keypath.min.js'></script>
<script src='/js/underscore-keypath.min.js'></script>
<script src='/js/bootbox.min.js'></script>
<!--#include virtual="page-end.html"-->

View file

@ -1399,6 +1399,11 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS
break;
}
case PacketTypeDomainServerPathQuery: {
// have our private method attempt to respond to this path query
respondToPathQuery(receivedPacket, senderSockAddr);
break;
}
case PacketTypeNodeJsonStats: {
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (matchingNode) {
@ -2191,3 +2196,75 @@ void DomainServer::addStaticAssignmentsToQueue() {
++staticAssignment;
}
}
void DomainServer::respondToPathQuery(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) {
// this is a query for the viewpoint resulting from a path
// first pull the query path from the packet
int numHeaderBytes = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerPathQuery);
const char* packetDataStart = receivedPacket.data() + numHeaderBytes;
// figure out how many bytes the sender said this path is
quint16 numPathBytes = *packetDataStart;
if (numPathBytes <= receivedPacket.size() - numHeaderBytes - sizeof(numPathBytes)) {
// the number of path bytes makes sense for the sent packet - pull out the path
QString pathQuery = QString::fromUtf8(packetDataStart + sizeof(numPathBytes), numPathBytes);
// our settings contain paths that start with a leading slash, so make sure this query has that
if (!pathQuery.startsWith("/")) {
pathQuery.prepend("/");
}
const QString PATHS_SETTINGS_KEYPATH_FORMAT = "%1.%2";
const QString PATH_VIEWPOINT_KEY = "viewpoint";
// check out paths in the _configMap to see if we have a match
const QVariant* pathMatch = valueForKeyPath(_settingsManager.getSettingsMap(),
QString(PATHS_SETTINGS_KEYPATH_FORMAT).arg(SETTINGS_PATHS_KEY)
.arg(pathQuery));
if (pathMatch) {
// we got a match, respond with the resulting viewpoint
auto nodeList = DependencyManager::get<LimitedNodeList>();
QString responseViewpoint = pathMatch->toMap()[PATH_VIEWPOINT_KEY].toString();
if (!responseViewpoint.isEmpty()) {
QByteArray viewpointUTF8 = responseViewpoint.toUtf8();
// prepare a packet for the response
QByteArray pathResponsePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeDomainServerPathResponse);
// check the number of bytes the viewpoint is
quint16 numViewpointBytes = responseViewpoint.toUtf8().size();
// are we going to be able to fit this response viewpoint in a packet?
if (numPathBytes + numViewpointBytes + pathResponsePacket.size() + sizeof(numViewpointBytes)
< MAX_PACKET_SIZE) {
// append the number of bytes this path is
pathResponsePacket.append(reinterpret_cast<char*>(&numPathBytes), sizeof(numPathBytes));
// append the path itself
pathResponsePacket.append(pathQuery.toUtf8());
// append the number of bytes the resulting viewpoint is
pathResponsePacket.append(reinterpret_cast<char*>(&numViewpointBytes), sizeof(numViewpointBytes));
// append the viewpoint itself
pathResponsePacket.append(viewpointUTF8);
qDebug() << "Sending a viewpoint response for path query" << pathQuery << "-" << viewpointUTF8;
// send off the packet - see if we can associate this outbound data to a particular node
// TODO: does this senderSockAddr always work for a punched DS client?
nodeList->writeUnverifiedDatagram(pathResponsePacket, senderSockAddr);
}
}
} else {
// we don't respond if there is no match - this may need to change once this packet
// query/response is made reliable
qDebug() << "No match for path query" << pathQuery << "- refusing to respond.";
}
}
}

View file

@ -112,6 +112,8 @@ private:
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
void addStaticAssignmentsToQueue();
void respondToPathQuery(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
QUrl oauthRedirectURL();
QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid());

View file

@ -32,6 +32,8 @@ const QString DESCRIPTION_NAME_KEY = "name";
const QString SETTING_DESCRIPTION_TYPE_KEY = "type";
const QString DESCRIPTION_COLUMNS_KEY = "columns";
const QString SETTINGS_VIEWPOINT_KEY = "viewpoint";
DomainServerSettingsManager::DomainServerSettingsManager() :
_descriptionArray(),
_configMap()
@ -130,10 +132,10 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection
QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent());
QJsonObject postedObject = postedDocument.object();
qDebug() << "The postedObject is" << postedObject;
qDebug() << "DomainServerSettingsManager postedObject -" << postedObject;
// we recurse one level deep below each group for the appropriate setting
recurseJSONObjectAndOverwriteSettings(postedObject, _configMap.getUserConfig(), _descriptionArray);
recurseJSONObjectAndOverwriteSettings(postedObject, _configMap.getUserConfig());
// store whatever the current _settingsMap is to file
persistToFile();
@ -201,31 +203,45 @@ QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& ty
// we need to check if the settings map has a value for this setting
QVariant variantValue;
QVariant settingsMapGroupValue = _configMap.getMergedConfig()
.value(groupObject[DESCRIPTION_NAME_KEY].toString());
if (!groupKey.isEmpty()) {
QVariant settingsMapGroupValue = _configMap.getMergedConfig().value(groupKey);
if (!settingsMapGroupValue.isNull()) {
variantValue = settingsMapGroupValue.toMap().value(settingName);
}
} else {
variantValue = _configMap.getMergedConfig().value(settingName);
}
QJsonValue result;
if (variantValue.isNull()) {
// no value for this setting, pass the default
if (settingObject.contains(SETTING_DEFAULT_KEY)) {
groupResponseObject[settingName] = settingObject[SETTING_DEFAULT_KEY];
result = settingObject[SETTING_DEFAULT_KEY];
} else {
// users are allowed not to provide a default for string values
// if so we set to the empty string
groupResponseObject[settingName] = QString("");
result = QString("");
}
} else {
groupResponseObject[settingName] = QJsonValue::fromVariant(variantValue);
result = QJsonValue::fromVariant(variantValue);
}
if (!groupKey.isEmpty()) {
// this belongs in the group object
groupResponseObject[settingName] = result;
} else {
// this is a value that should be at the root
responseObject[settingName] = result;
}
}
}
}
if (!groupResponseObject.isEmpty()) {
if (!groupKey.isEmpty() && !groupResponseObject.isEmpty()) {
// set this group's object to the constructed object
responseObject[groupKey] = groupResponseObject;
}
@ -236,25 +252,6 @@ QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& ty
return responseObject;
}
bool DomainServerSettingsManager::settingExists(const QString& groupName, const QString& settingName,
const QJsonArray& descriptionArray, QJsonObject& settingDescription) {
foreach(const QJsonValue& groupValue, descriptionArray) {
QJsonObject groupObject = groupValue.toObject();
if (groupObject[DESCRIPTION_NAME_KEY].toString() == groupName) {
foreach(const QJsonValue& settingValue, groupObject[DESCRIPTION_SETTINGS_KEY].toArray()) {
QJsonObject settingObject = settingValue.toObject();
if (settingObject[DESCRIPTION_NAME_KEY].toString() == settingName) {
settingDescription = settingObject;
return true;
}
}
}
}
settingDescription = QJsonObject();
return false;
}
void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
const QJsonObject& settingDescription) {
if (newValue.isString()) {
@ -272,7 +269,15 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV
} else if (settingType == INPUT_INTEGER_TYPE) {
settingMap[key] = newValue.toString().toInt();
} else {
settingMap[key] = newValue.toString();
QString sanitizedValue = newValue.toString();
// we perform special handling for viewpoints here
// we do not want them to be prepended with a slash
if (key == SETTINGS_VIEWPOINT_KEY && !sanitizedValue.startsWith('/')) {
sanitizedValue.prepend('/');
}
settingMap[key] = sanitizedValue;
}
}
} else if (newValue.isBool()) {
@ -302,7 +307,15 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV
}
}
updateSetting(childKey, newValue.toObject()[childKey], thisMap, childDescriptionObject);
QString sanitizedKey = childKey;
if (key == SETTINGS_PATHS_KEY && !sanitizedKey.startsWith('/')) {
// We perform special handling for paths here.
// If we got sent a path without a leading slash then we add it.
sanitizedKey.prepend("/");
}
updateSetting(sanitizedKey, newValue.toObject()[childKey], thisMap, childDescriptionObject);
}
if (settingMap[key].toMap().isEmpty()) {
@ -316,32 +329,89 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV
}
}
QJsonObject DomainServerSettingsManager::settingDescriptionFromGroup(const QJsonObject& groupObject, const QString& settingName) {
foreach(const QJsonValue& settingValue, groupObject[DESCRIPTION_SETTINGS_KEY].toArray()) {
QJsonObject settingObject = settingValue.toObject();
if (settingObject[DESCRIPTION_NAME_KEY].toString() == settingName) {
return settingObject;
}
}
return QJsonObject();
}
void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject,
QVariantMap& settingsVariant,
const QJsonArray& descriptionArray) {
QVariantMap& settingsVariant) {
// Iterate on the setting groups
foreach(const QString& groupKey, postedObject.keys()) {
QJsonValue groupValue = postedObject[groupKey];
foreach(const QString& rootKey, postedObject.keys()) {
QJsonValue rootValue = postedObject[rootKey];
if (!settingsVariant.contains(groupKey)) {
if (!settingsVariant.contains(rootKey)) {
// we don't have a map below this key yet, so set it up now
settingsVariant[groupKey] = QVariantMap();
settingsVariant[rootKey] = QVariantMap();
}
// Iterate on the settings
foreach(const QString& settingKey, groupValue.toObject().keys()) {
QJsonValue settingValue = groupValue.toObject()[settingKey];
QVariantMap& thisMap = settingsVariant;
QJsonObject thisDescription;
if (settingExists(groupKey, settingKey, descriptionArray, thisDescription)) {
QVariantMap& thisMap = *reinterpret_cast<QVariantMap*>(settingsVariant[groupKey].data());
updateSetting(settingKey, settingValue, thisMap, thisDescription);
QJsonObject groupDescriptionObject;
// we need to check the description array to see if this is a root setting or a group setting
foreach(const QJsonValue& groupValue, _descriptionArray) {
if (groupValue.toObject()[DESCRIPTION_NAME_KEY] == rootKey) {
// we matched a group - keep this since we'll use it below to update the settings
groupDescriptionObject = groupValue.toObject();
// change the map we will update to be the map for this group
thisMap = *reinterpret_cast<QVariantMap*>(settingsVariant[rootKey].data());
break;
}
}
if (settingsVariant[groupKey].toMap().empty()) {
if (groupDescriptionObject.isEmpty()) {
// this is a root value, so we can call updateSetting for it directly
// first we need to find our description value for it
QJsonObject matchingDescriptionObject;
foreach(const QJsonValue& groupValue, _descriptionArray) {
// find groups with root values (they don't have a group name)
QJsonObject groupObject = groupValue.toObject();
if (!groupObject.contains(DESCRIPTION_NAME_KEY)) {
// this is a group with root values - check if our setting is in here
matchingDescriptionObject = settingDescriptionFromGroup(groupObject, rootKey);
if (!matchingDescriptionObject.isEmpty()) {
break;
}
}
}
if (!matchingDescriptionObject.isEmpty()) {
updateSetting(rootKey, rootValue, thisMap, matchingDescriptionObject);
} else {
qDebug() << "Setting for root key" << rootKey << "does not exist - cannot update setting.";
}
} else {
// this is a group - iterate on the settings in the group
foreach(const QString& settingKey, rootValue.toObject().keys()) {
// make sure this particular setting exists and we have a description object for it
QJsonObject matchingDescriptionObject = settingDescriptionFromGroup(groupDescriptionObject, settingKey);
// if we matched the setting then update the value
if (!matchingDescriptionObject.isEmpty()) {
QJsonValue settingValue = rootValue.toObject()[settingKey];
updateSetting(settingKey, settingValue, thisMap, matchingDescriptionObject);
} else {
qDebug() << "Could not find description for setting" << settingKey << "in group" << rootKey <<
"- cannot update setting.";
}
}
}
if (settingsVariant[rootKey].toMap().empty()) {
// we've cleared all of the settings below this value, so remove this one too
settingsVariant.remove(groupKey);
settingsVariant.remove(rootKey);
}
}
}

View file

@ -18,6 +18,8 @@
#include <HifiConfigVariantMap.h>
#include <HTTPManager.h>
const QString SETTINGS_PATHS_KEY = "paths";
class DomainServerSettingsManager : public QObject {
Q_OBJECT
public:
@ -31,12 +33,11 @@ public:
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
private:
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);
void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant,
const QJsonArray& descriptionArray);
bool settingExists(const QString& groupName, const QString& settingName,
const QJsonArray& descriptionArray, QJsonObject& settingDescription);
void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant);
void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
const QJsonObject& settingDescription);
QJsonObject settingDescriptionFromGroup(const QJsonObject& groupObject, const QString& settingName);
void persistToFile();
QJsonArray _descriptionArray;

View file

@ -260,9 +260,11 @@ function update(deltaTime) {
}
Entities.editEntity(grabbedEntity, {
position: currentPosition,
rotation: currentRotation,
velocity: newVelocity,
angularVelocity: angularVelocity
})
});
updateDropLine(targetPosition);
}
}

View file

@ -633,7 +633,7 @@
elColorBlue.addEventListener('change', colorChangeFunction);
$('#property-color').colpick({
colorScheme:'dark',
layout:'rgbhex',
layout:'hex',
color:'000000',
onSubmit: function(hsb, hex, rgb, el) {
$(el).css('background-color', '#'+hex);
@ -651,7 +651,7 @@
elLightColorBlue.addEventListener('change', lightColorChangeFunction);
$('#property-light-color').colpick({
colorScheme:'dark',
layout:'rgbhex',
layout:'hex',
color:'000000',
onSubmit: function(hsb, hex, rgb, el) {
$(el).css('background-color', '#'+hex);
@ -686,7 +686,7 @@
elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction);
$('#property-text-text-color').colpick({
colorScheme:'dark',
layout:'rgbhex',
layout:'hex',
color:'000000',
onSubmit: function(hsb, hex, rgb, el) {
$(el).css('background-color', '#'+hex);
@ -702,7 +702,7 @@
elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
$('#property-text-background-color').colpick({
colorScheme:'dark',
layout:'rgbhex',
layout:'hex',
color:'000000',
onSubmit: function(hsb, hex, rgb, el) {
$(el).css('background-color', '#'+hex);
@ -714,7 +714,7 @@
elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled'));
$('#property-zone-key-light-color').colpick({
colorScheme:'dark',
layout:'rgbhex',
layout:'hex',
color:'000000',
onSubmit: function(hsb, hex, rgb, el) {
$(el).css('background-color', '#'+hex);
@ -751,7 +751,7 @@
elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction);
$('#property-zone-skybox-color').colpick({
colorScheme:'dark',
layout:'rgbhex',
layout:'hex',
color:'000000',
onSubmit: function(hsb, hex, rgb, el) {
$(el).css('background-color', '#'+hex);

View file

@ -1366,12 +1366,12 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
//#define VR_MENU_ONLY_IN_HMD
#define VR_MENU_ONLY_IN_HMD
void Application::keyReleaseEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Alt && _altPressed && _window->isActiveWindow()) {
#ifdef VR_MENU_ONLY_IN_HMD
if (OculusManager::isConnected()) {
if (isHMDMode()) {
#endif
VrMenu::toggle();
#ifdef VR_MENU_ONLY_IN_HMD
@ -1949,6 +1949,7 @@ void Application::setActiveFaceTracker() {
#endif
#ifdef HAVE_DDE
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera);
Menu::getInstance()->getActionForOption(MenuOption::BinaryEyelidControl)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::UseAudioForMouth)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::CalibrateCamera)->setVisible(isUsingDDE);

View file

@ -388,6 +388,8 @@ Menu::Menu() {
}
#ifdef HAVE_DDE
faceTrackingMenu->addSeparator();
QAction* binaryEyelidControl = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::BinaryEyelidControl, 0, true);
binaryEyelidControl->setVisible(false);
QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true);
useAudioForMouth->setVisible(false);
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);

View file

@ -149,6 +149,7 @@ namespace MenuOption {
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
const QString AvatarReceiveStats = "Show Receive Stats";
const QString BandwidthDetails = "Bandwidth Details";
const QString BinaryEyelidControl = "Binary Eyelid Control";
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
const QString BookmarkLocation = "Bookmark Location";
const QString Bookmarks = "Bookmarks";

View file

@ -137,7 +137,7 @@ struct Packet {
};
static const float STARTING_DDE_MESSAGE_TIME = 0.033f;
static const float DEFAULT_DDE_EYE_CLOSING_THRESHOLD = 0.8f;
static const int CALIBRATION_SAMPLES = 150;
#ifdef WIN32
@ -182,6 +182,7 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui
_lastEyeBlinks(),
_filteredEyeBlinks(),
_lastEyeCoefficients(),
_eyeClosingThreshold("ddeEyeClosingThreshold", DEFAULT_DDE_EYE_CLOSING_THRESHOLD),
_isCalibrating(false),
_calibrationCount(0),
_calibrationValues(),
@ -194,8 +195,8 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui
_coefficientAverages.resize(NUM_FACESHIFT_BLENDSHAPES);
_calibrationValues.resize(NUM_FACESHIFT_BLENDSHAPES);
_eyeStates[0] = EYE_OPEN;
_eyeStates[1] = EYE_OPEN;
_eyeStates[0] = EYE_UNCONTROLLED;
_eyeStates[1] = EYE_UNCONTROLLED;
connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError)));
@ -450,6 +451,12 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
// Finesse EyeBlink values
float eyeCoefficients[2];
if (Menu::getInstance()->isOptionChecked(MenuOption::BinaryEyelidControl)) {
if (_eyeStates[0] == EYE_UNCONTROLLED) {
_eyeStates[0] = EYE_OPEN;
_eyeStates[1] = EYE_OPEN;
}
for (int i = 0; i < 2; i++) {
// Scale EyeBlink values so that they can be used to control both EyeBlink and EyeOpen
// -ve values control EyeOpen; +ve values control EyeBlink
@ -458,12 +465,12 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
// Change to closing or opening states
const float EYE_CONTROL_HYSTERISIS = 0.25f;
const float EYE_CLOSING_THRESHOLD = 0.8f;
const float EYE_OPENING_THRESHOLD = EYE_CONTROL_THRESHOLD - EYE_CONTROL_HYSTERISIS;
if ((_eyeStates[i] == EYE_OPEN || _eyeStates[i] == EYE_OPENING) && eyeCoefficients[i] > EYE_CLOSING_THRESHOLD) {
float eyeClosingThreshold = getEyeClosingThreshold();
float eyeOpeningThreshold = eyeClosingThreshold - EYE_CONTROL_HYSTERISIS;
if ((_eyeStates[i] == EYE_OPEN || _eyeStates[i] == EYE_OPENING) && eyeCoefficients[i] > eyeClosingThreshold) {
_eyeStates[i] = EYE_CLOSING;
} else if ((_eyeStates[i] == EYE_CLOSED || _eyeStates[i] == EYE_CLOSING)
&& eyeCoefficients[i] < EYE_OPENING_THRESHOLD) {
&& eyeCoefficients[i] < eyeOpeningThreshold) {
_eyeStates[i] = EYE_OPENING;
}
@ -495,12 +502,21 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
eyeCoefficients[i] = 1.0;
}
}
if (_eyeStates[0] == EYE_OPEN && _eyeStates[1] == EYE_OPEN) {
// Couple eyelids
eyeCoefficients[0] = eyeCoefficients[1] = (eyeCoefficients[0] + eyeCoefficients[0]) / 2.0f;
}
_lastEyeCoefficients[0] = eyeCoefficients[0];
_lastEyeCoefficients[1] = eyeCoefficients[1];
} else {
_eyeStates[0] = EYE_UNCONTROLLED;
_eyeStates[1] = EYE_UNCONTROLLED;
eyeCoefficients[0] = _filteredEyeBlinks[0];
eyeCoefficients[1] = _filteredEyeBlinks[1];
}
// Use EyeBlink values to control both EyeBlink and EyeOpen
if (eyeCoefficients[0] > 0) {
@ -544,6 +560,10 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
}
}
void DdeFaceTracker::setEyeClosingThreshold(float eyeClosingThreshold) {
_eyeClosingThreshold.set(eyeClosingThreshold);
}
static const int CALIBRATION_BILLBOARD_WIDTH = 240;
static const int CALIBRATION_BILLBOARD_HEIGHT = 180;
static const int CALIBRATION_BILLBOARD_TOP_MARGIN = 60;

View file

@ -50,6 +50,9 @@ public:
float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); }
float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); }
float getEyeClosingThreshold() { return _eyeClosingThreshold.get(); }
void setEyeClosingThreshold(float eyeClosingThreshold);
public slots:
void setEnabled(bool enabled);
void calibrate();
@ -90,7 +93,6 @@ private:
int _leftEyeOpenIndex;
int _rightEyeOpenIndex;
// Brows
int _browDownLeftIndex;
int _browDownRightIndex;
int _browUpCenterIndex;
@ -114,6 +116,7 @@ private:
float _filteredBrowUp;
enum EyeState {
EYE_UNCONTROLLED,
EYE_OPEN,
EYE_CLOSING,
EYE_CLOSED,
@ -123,6 +126,8 @@ private:
float _lastEyeBlinks[2];
float _filteredEyeBlinks[2];
float _lastEyeCoefficients[2];
Setting::Handle<float> _eyeClosingThreshold;
QVector<float> _coefficientAverages;
bool _isCalibrating;

View file

@ -14,6 +14,7 @@
#include <AudioClient.h>
#include <avatar/AvatarManager.h>
#include <devices/DdeFaceTracker.h>
#include <devices/Faceshift.h>
#include <devices/SixenseManager.h>
#include <NetworkingConstants.h>
@ -135,6 +136,10 @@ void PreferencesDialog::loadPreferences() {
ui.pupilDilationSlider->setValue(myAvatar->getHead()->getPupilDilation() *
ui.pupilDilationSlider->maximum());
auto dde = DependencyManager::get<DdeFaceTracker>();
ui.ddeEyeClosingThresholdSlider->setValue(dde->getEyeClosingThreshold() *
ui.ddeEyeClosingThresholdSlider->maximum());
auto faceshift = DependencyManager::get<Faceshift>();
ui.faceshiftEyeDeflectionSider->setValue(faceshift->getEyeDeflection() *
ui.faceshiftEyeDeflectionSider->maximum());
@ -222,6 +227,10 @@ void PreferencesDialog::savePreferences() {
qApp->setFieldOfView(ui.fieldOfViewSpin->value());
auto dde = DependencyManager::get<DdeFaceTracker>();
dde->setEyeClosingThreshold(ui.ddeEyeClosingThresholdSlider->value() /
(float)ui.ddeEyeClosingThresholdSlider->maximum());
auto faceshift = DependencyManager::get<Faceshift>();
faceshift->setEyeDeflection(ui.faceshiftEyeDeflectionSider->value() /
(float)ui.faceshiftEyeDeflectionSider->maximum());

View file

@ -1256,7 +1256,7 @@
</font>
</property>
<property name="text">
<string>Pupil dillation</string>
<string>Pupil dilation</string>
</property>
<property name="indent">
<number>0</number>
@ -1310,6 +1310,82 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_28">
<property name="spacing">
<number>0</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>7</number>
</property>
<item alignment="Qt::AlignLeft">
<widget class="QLabel" name="label_7">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="text">
<string>Camera binary eyelid threshold</string>
</property>
<property name="indent">
<number>0</number>
</property>
<property name="buddy">
<cstring>ddeEyeClosingThresholdSlider</cstring>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_8">
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSlider" name="ddeEyeClosingThresholdSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>130</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="spacing">

View file

@ -120,12 +120,11 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
if (handleNetworkAddress(lookupUrl.host()
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) {
// we may have a path that defines a relative viewpoint - if so we should jump to that now
handleRelativeViewpoint(lookupUrl.path());
handlePath(lookupUrl.path());
} else {
// wasn't an address - lookup the place name
// we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after
attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path());
}
}
@ -134,7 +133,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
qCDebug(networking) << "Going to relative path" << lookupUrl.path();
// if this is a relative path then handle it as a relative viewpoint
handleRelativeViewpoint(lookupUrl.path());
handlePath(lookupUrl.path());
emit lookupResultsFinished();
}
@ -239,9 +238,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
if (!overridePath.isEmpty()) {
if (!handleRelativeViewpoint(overridePath)){
qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << overridePath;
}
handlePath(overridePath);
} else {
// take the path that came back
const QString PLACE_PATH_KEY = "path";
@ -251,12 +248,16 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
if (!returnedPath.isEmpty()) {
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
if (!handleRelativeViewpoint(returnedPath, shouldFaceViewpoint)) {
qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -" << returnedPath;
if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) {
qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -"
<< returnedPath;
}
} else {
// we didn't override the path or get one back - ask the DS for the viewpoint of its index path
// which we will jump to if it exists
emit pathChangeRequired(INDEX_PATH);
}
}
}
} else {
qCDebug(networking) << "Received an address manager API response with no domain key. Cannot parse.";
@ -342,7 +343,15 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) {
return false;
}
bool AddressManager::handleRelativeViewpoint(const QString& lookupString, bool shouldFace) {
void AddressManager::handlePath(const QString& path) {
if (!handleViewpoint(path)) {
qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path <<
"- wll attempt to ask domain-server to resolve.";
emit pathChangeRequired(path);
}
}
bool AddressManager::handleViewpoint(const QString& viewpointString, bool shouldFace) {
const QString FLOAT_REGEX_STRING = "([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
const QString SPACED_COMMA_REGEX_STRING = "\\s*,\\s*";
const QString POSITION_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
@ -353,7 +362,7 @@ bool AddressManager::handleRelativeViewpoint(const QString& lookupString, bool s
QRegExp positionRegex(POSITION_REGEX_STRING);
if (positionRegex.indexIn(lookupString) != -1) {
if (positionRegex.indexIn(viewpointString) != -1) {
// we have at least a position, so emit our signal to say we need to change position
glm::vec3 newPosition(positionRegex.cap(1).toFloat(),
positionRegex.cap(2).toFloat(),
@ -365,8 +374,8 @@ bool AddressManager::handleRelativeViewpoint(const QString& lookupString, bool s
QRegExp orientationRegex(QUAT_REGEX_STRING);
// we may also have an orientation
if (lookupString[positionRegex.matchedLength() - 1] == QChar('/')
&& orientationRegex.indexIn(lookupString, positionRegex.matchedLength() - 1) != -1) {
if (viewpointString[positionRegex.matchedLength() - 1] == QChar('/')
&& orientationRegex.indexIn(viewpointString, positionRegex.matchedLength() - 1) != -1) {
glm::quat newOrientation = glm::normalize(glm::quat(orientationRegex.cap(4).toFloat(),
orientationRegex.cap(1).toFloat(),
@ -389,10 +398,10 @@ bool AddressManager::handleRelativeViewpoint(const QString& lookupString, bool s
}
return true;
}
} else {
return false;
}
}
const QString GET_USER_LOCATION = "/api/v1/users/%1/location";

View file

@ -24,6 +24,7 @@
const QString HIFI_URL_SCHEME = "hifi";
const QString DEFAULT_HIFI_ADDRESS = "hifi://entry";
const QString INDEX_PATH = "/";
typedef const glm::vec3& (*PositionGetter)();
typedef glm::quat (*OrientationGetter)();
@ -57,8 +58,10 @@ public:
public slots:
void handleLookupString(const QString& lookupString);
void goToUser(const QString& username);
void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply);
bool goToViewpoint(const QString& viewpointString) { return handleViewpoint(viewpointString); }
void storeCurrentAddress();
@ -74,6 +77,7 @@ signals:
void locationChangeRequired(const glm::vec3& newPosition,
bool hasOrientationChange, const glm::quat& newOrientation,
bool shouldFaceLocation);
void pathChangeRequired(const QString& newPath);
void rootPlaceNameChanged(const QString& newRootPlaceName);
protected:
AddressManager();
@ -88,7 +92,8 @@ private:
bool handleUrl(const QUrl& lookupUrl);
bool handleNetworkAddress(const QString& lookupString);
bool handleRelativeViewpoint(const QString& pathSubsection, bool shouldFace = false);
void handlePath(const QString& path);
bool handleViewpoint(const QString& viewpointString, bool shouldFace = false);
bool handleUsername(const QString& lookupString);
QString _rootPlaceName;

View file

@ -46,7 +46,7 @@ void DomainHandler::clearConnectionInfo() {
if (requiresICE()) {
// if we connected to this domain with ICE, re-set the socket so we reconnect through the ice-server
_sockAddr.setAddress(QHostAddress::Null);
_sockAddr.clear();
}
setIsConnected(false);
@ -70,7 +70,10 @@ void DomainHandler::hardReset() {
_iceDomainID = QUuid();
_iceServerSockAddr = HifiSockAddr();
_hostname = QString();
_sockAddr.setAddress(QHostAddress::Null);
_sockAddr.clear();
// clear any pending path we may have wanted to ask the previous DS about
_pendingPath.clear();
}
void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname) {
@ -142,19 +145,25 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
void DomainHandler::activateICELocalSocket() {
_sockAddr = _icePeer.getLocalSocket();
_hostname = _sockAddr.getAddress().toString();
emit completedSocketDiscovery();
}
void DomainHandler::activateICEPublicSocket() {
_sockAddr = _icePeer.getPublicSocket();
_hostname = _sockAddr.getAddress().toString();
emit completedSocketDiscovery();
}
void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
for (int i = 0; i < hostInfo.addresses().size(); i++) {
if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
_sockAddr.setAddress(hostInfo.addresses()[i]);
qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(),
_sockAddr.getAddress().toString().toLocal8Bit().constData());
emit completedSocketDiscovery();
return;
}
}

View file

@ -72,6 +72,12 @@ public:
void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket);
void processICEResponsePacket(const QByteArray& icePacket);
void setPendingPath(const QString& pendingPath) { _pendingPath = pendingPath; }
const QString& getPendingPath() { return _pendingPath; }
void clearPendingPath() { _pendingPath.clear(); }
bool isSocketKnown() const { return !_sockAddr.getAddress().isNull(); }
void softReset();
public slots:
void setHostnameAndPort(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT);
@ -82,8 +88,14 @@ private slots:
void settingsRequestFinished();
signals:
void hostnameChanged(const QString& hostname);
// NOTE: the emission of completedSocketDiscovery does not mean a connection to DS is established
// It means that, either from DNS lookup or ICE, we think we have a socket we can talk to DS on
void completedSocketDiscovery();
void connectedToDomain(const QString& hostname);
void disconnectedFromDomain();
void requestICEConnectionAttempt();
void settingsReceived(const QJsonObject& domainSettingsObject);
@ -103,6 +115,7 @@ private:
bool _isConnected;
QJsonObject _settingsObject;
int _failedSettingsRequests;
QString _pendingPath;
};
#endif // hifi_DomainHandler_h

View file

@ -31,6 +31,7 @@ public:
HifiSockAddr(const sockaddr* sockaddr);
bool isNull() const { return _address.isNull() && _port == 0; }
void clear() { _address = QHostAddress::Null; _port = 0;}
HifiSockAddr& operator=(const HifiSockAddr& rhsSockAddr);
void swap(HifiSockAddr& otherSockAddr);

View file

@ -54,6 +54,13 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID,
&_domainHandler, &DomainHandler::setIceServerHostnameAndID);
// handle a request for a path change from the AddressManager
connect(addressManager.data(), &AddressManager::pathChangeRequired, this, &NodeList::handleDSPathQuery);
// in case we don't know how to talk to DS when a path change is requested
// fire off any pending DS path query when we get socket information
connect(&_domainHandler, &DomainHandler::completedSocketDiscovery, this, &NodeList::sendPendingDSPathQuery);
// clear our NodeList when the domain changes
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset);
@ -226,6 +233,10 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
processSTUNResponse(packet);
break;
}
case PacketTypeDomainServerPathResponse: {
handleDSPathQueryResponse(packet);
break;
}
default:
LimitedNodeList::processNodeData(senderSockAddr, packet);
break;
@ -395,6 +406,102 @@ void NodeList::sendDomainServerCheckIn() {
}
}
void NodeList::handleDSPathQuery(const QString& newPath) {
if (_domainHandler.isSocketKnown()) {
// if we have a DS socket we assume it will get this packet and send if off right away
sendDSPathQuery(newPath);
} else {
// otherwise we make it pending so that it can be sent once a connection is established
_domainHandler.setPendingPath(newPath);
}
}
void NodeList::sendPendingDSPathQuery() {
QString pendingPath = _domainHandler.getPendingPath();
if (!pendingPath.isEmpty()) {
qCDebug(networking) << "Attemping to send pending query to DS for path" << pendingPath;
// this is a slot triggered if we just established a network link with a DS and want to send a path query
sendDSPathQuery(_domainHandler.getPendingPath());
// clear whatever the pending path was
_domainHandler.clearPendingPath();
}
}
void NodeList::sendDSPathQuery(const QString& newPath) {
// only send a path query if we know who our DS is or is going to be
if (_domainHandler.isSocketKnown()) {
// construct the path query packet
QByteArray pathQueryPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerPathQuery);
// get the UTF8 representation of path query
QByteArray pathQueryUTF8 = newPath.toUtf8();
// get the size of the UTF8 representation of the desired path
quint16 numPathBytes = pathQueryUTF8.size();
if (pathQueryPacket.size() + numPathBytes + sizeof(numPathBytes) < MAX_PACKET_SIZE) {
// append the size of the path to the query packet
pathQueryPacket.append(reinterpret_cast<char*>(&numPathBytes), sizeof(numPathBytes));
// append the path itself to the query packet
pathQueryPacket.append(pathQueryUTF8);
qCDebug(networking) << "Sending a path query packet for path" << newPath << "to domain-server at"
<< _domainHandler.getSockAddr();
// send off the path query
writeUnverifiedDatagram(pathQueryPacket, _domainHandler.getSockAddr());
} else {
qCDebug(networking) << "Path" << newPath << "would make PacketTypeDomainServerPathQuery packet > MAX_PACKET_SIZE." <<
"Will not send query.";
}
}
}
void NodeList::handleDSPathQueryResponse(const QByteArray& packet) {
// This is a response to a path query we theoretically made.
// In the future we may want to check that this was actually from our DS and for a query we actually made.
int numHeaderBytes = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerPathResponse);
const char* startPosition = packet.data() + numHeaderBytes;
const char* currentPosition = startPosition;
// figure out how many bytes the path query is
qint16 numPathBytes;
memcpy(&numPathBytes, currentPosition, sizeof(numPathBytes));
currentPosition += sizeof(numPathBytes);
// make sure it is safe to pull the path
if (numPathBytes <= packet.size() - numHeaderBytes - (currentPosition - startPosition)) {
// pull the path from the packet
QString pathQuery = QString::fromUtf8(currentPosition, numPathBytes);
currentPosition += numPathBytes;
// figure out how many bytes the viewpoint is
qint16 numViewpointBytes;
memcpy(&numViewpointBytes, currentPosition, sizeof(numViewpointBytes));
currentPosition += sizeof(numViewpointBytes);
// make sure it is safe to pull the viewpoint
if (numViewpointBytes <= packet.size() - numHeaderBytes - (currentPosition - startPosition)) {
// pull the viewpoint from the packet
QString viewpoint = QString::fromUtf8(currentPosition, numViewpointBytes);
// Hand it off to the AddressManager so it can handle it as a relative viewpoint
if (DependencyManager::get<AddressManager>()->goToViewpoint(viewpoint)) {
qCDebug(networking) << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery;
} else {
qCDebug(networking) << "Could not go to viewpoint" << viewpoint
<< "which was the lookup result for path" << pathQuery;
}
}
}
}
void NodeList::handleICEConnectionToDomainServer() {
if (_domainHandler.getICEPeer().isNull()
|| _domainHandler.getICEPeer().getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) {

View file

@ -71,8 +71,11 @@ public slots:
void reset();
void sendDomainServerCheckIn();
void pingInactiveNodes();
void handleDSPathQuery(const QString& newPath);
signals:
void limitOfSilentDomainCheckInsReached();
private slots:
void sendPendingDSPathQuery();
private:
NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile
NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
@ -89,6 +92,10 @@ private:
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
void timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode);
void handleDSPathQueryResponse(const QByteArray& packet);
void sendDSPathQuery(const QString& newPath);
NodeType_t _ownerType;
NodeSet _nodeTypesOfInterest;
DomainHandler _domainHandler;

View file

@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
return 1;
case PacketTypeEntityAddOrEdit:
case PacketTypeEntityData:
return VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES;
return VERSION_ENTITIES_HAVE_LINE_TYPE;
case PacketTypeEntityErase:
return 2;
case PacketTypeAudioStreamStats:

View file

@ -47,8 +47,8 @@ enum PacketType {
PacketTypeMuteEnvironment,
PacketTypeAudioStreamStats,
PacketTypeDataServerConfirm, // 20
UNUSED_1,
UNUSED_2,
PacketTypeDomainServerPathQuery,
PacketTypeDomainServerPathResponse,
UNUSED_3,
UNUSED_4,
UNUSED_5, // 25
@ -96,7 +96,8 @@ const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
<< PacketTypeNodeJsonStats << PacketTypeEntityQuery
<< PacketTypeOctreeDataNack << PacketTypeEntityEditNack
<< PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse
<< PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode;
<< PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode
<< PacketTypeDomainServerPathQuery << PacketTypeDomainServerPathResponse;
const QSet<PacketType> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType>()
<< PacketTypeAvatarData;
@ -174,5 +175,6 @@ const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_ATMOSPHERE = 20;
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_SKYBOX = 21;
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_STAGE_HAS_AUTOMATIC_HOURDAY = 22;
const PacketVersion VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES = 23;
const PacketVersion VERSION_ENTITIES_HAVE_LINE_TYPE = 24;
#endif // hifi_PacketHeaders_h

View file

@ -159,7 +159,7 @@ void HifiConfigVariantMap::addMissingValuesToExistingMap(QVariantMap& existingMa
}
}
const QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath) {
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath) {
int dotIndex = keyPath.indexOf('.');
QString firstKey = (dotIndex == -1) ? keyPath : keyPath.mid(0, dotIndex);

View file

@ -37,6 +37,6 @@ private:
void addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap);
};
const QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath);
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath);
#endif // hifi_HifiConfigVariantMap_h

View file

@ -51,15 +51,16 @@ void InfoView::show(const QString& path, bool firstOrChangedOnly) {
}
if (firstOrChangedOnly) {
const QString lastVersion = infoVersion.get();
const QString version = fetchVersion(url);
// If we have version information stored
if (lastVersion != QString::null) {
// Check to see the document version. If it's valid and matches
// the stored version, we're done, so exit
const QString version = fetchVersion(url);
if (version == QString::null || version == lastVersion) {
return;
}
}
infoVersion.set(version);
}
auto offscreenUi = DependencyManager::get<OffscreenUi>();
QString infoViewName(NAME + "_" + path);