mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-07-23 07:44:11 +02:00
Merge remote-tracking branch 'upstream/master' into web-entity
This commit is contained in:
commit
9b6c20d93c
31 changed files with 1348 additions and 879 deletions
|
@ -1,7 +1,7 @@
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "metaverse",
|
"name": "metaverse",
|
||||||
"label": "Metaverse Registration",
|
"label": "Metaverse / Networking",
|
||||||
"settings": [
|
"settings": [
|
||||||
{
|
{
|
||||||
"name": "access_token",
|
"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",
|
"name": "security",
|
||||||
"label": "Security",
|
"label": "Security",
|
||||||
|
|
|
@ -21,34 +21,34 @@ var Settings = {
|
||||||
};
|
};
|
||||||
|
|
||||||
var viewHelpers = {
|
var viewHelpers = {
|
||||||
getFormGroup: function(groupName, setting, values, isAdvanced, isLocked) {
|
getFormGroup: function(keypath, setting, values, isAdvanced, isLocked) {
|
||||||
setting_name = groupName + "." + setting.name
|
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)) {
|
if (!setting_value) {
|
||||||
setting_value = values[groupName][setting.name]
|
if (_.has(setting, 'default')) {
|
||||||
} else if (_.has(setting, 'default')) {
|
setting_value = setting.default;
|
||||||
setting_value = setting.default
|
} else {
|
||||||
} else {
|
setting_value = "";
|
||||||
setting_value = ""
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
label_class = 'control-label'
|
label_class = 'control-label';
|
||||||
if (isLocked) {
|
if (isLocked) {
|
||||||
label_class += ' locked'
|
label_class += ' locked';
|
||||||
}
|
}
|
||||||
|
|
||||||
common_attrs = " class='" + (setting.type !== 'checkbox' ? 'form-control' : '')
|
common_attrs = " class='" + (setting.type !== 'checkbox' ? 'form-control' : '')
|
||||||
+ " " + Settings.TRIGGER_CHANGE_CLASS + "' data-short-name='" + setting.name + "' name='" + setting_name + "' "
|
+ " " + Settings.TRIGGER_CHANGE_CLASS + "' data-short-name='" + setting.name + "' name='" + keypath + "' "
|
||||||
+ "id='" + setting_name + "'"
|
+ "id='" + keypath + "'";
|
||||||
|
|
||||||
if (setting.type === 'checkbox') {
|
if (setting.type === 'checkbox') {
|
||||||
if (setting.label) {
|
if (setting.label) {
|
||||||
form_group += "<label class='" + label_class + "'>" + setting.label + "</label>"
|
form_group += "<label class='" + label_class + "'>" + setting.label + "</label>"
|
||||||
}
|
}
|
||||||
form_group += "<div class='checkbox" + (isLocked ? " disabled" : "") + "'>"
|
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 += "<input type='checkbox'" + common_attrs + (setting_value ? "checked" : "") + (isLocked ? " disabled" : "") + "/>"
|
||||||
form_group += " " + setting.help + "</label>";
|
form_group += " " + setting.help + "</label>";
|
||||||
form_group += "</div>"
|
form_group += "</div>"
|
||||||
|
@ -56,52 +56,52 @@ var viewHelpers = {
|
||||||
input_type = _.has(setting, 'type') ? setting.type : "text"
|
input_type = _.has(setting, 'type') ? setting.type : "text"
|
||||||
|
|
||||||
if (setting.label) {
|
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') {
|
if (input_type === 'table') {
|
||||||
form_group += makeTable(setting, setting_name, setting_value, isLocked)
|
form_group += makeTable(setting, keypath, setting_value, isLocked)
|
||||||
} else {
|
} else {
|
||||||
if (input_type === 'select') {
|
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) {
|
_.each(setting.options, function(option) {
|
||||||
form_group += "<option value='" + option.value + "'" +
|
form_group += "<option value='" + option.value + "'" +
|
||||||
(option.value == setting_value ? 'selected' : '') + ">" + option.label + "</option>"
|
(option.value == setting_value ? 'selected' : '') + ">" + option.label + "</option>"
|
||||||
})
|
})
|
||||||
|
|
||||||
form_group += "</select>"
|
form_group += "</select>"
|
||||||
|
|
||||||
form_group += "<input type='hidden'" + common_attrs + "value='" + setting_value + "'>"
|
form_group += "<input type='hidden'" + common_attrs + "value='" + setting_value + "'>"
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (input_type == 'integer') {
|
if (input_type == 'integer') {
|
||||||
input_type = "text"
|
input_type = "text"
|
||||||
}
|
}
|
||||||
|
|
||||||
form_group += "<input type='" + input_type + "'" + common_attrs +
|
form_group += "<input type='" + input_type + "'" + common_attrs +
|
||||||
"placeholder='" + (_.has(setting, 'placeholder') ? setting.placeholder : "") +
|
"placeholder='" + (_.has(setting, 'placeholder') ? setting.placeholder : "") +
|
||||||
"' value='" + setting_value + "'" + (isLocked ? " disabled" : "") + "/>"
|
"' value='" + setting_value + "'" + (isLocked ? " disabled" : "") + "/>"
|
||||||
}
|
}
|
||||||
|
|
||||||
form_group += "<span class='help-block'>" + setting.help + "</span>"
|
form_group += "<span class='help-block'>" + setting.help + "</span>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
form_group += "</div>"
|
form_group += "</div>"
|
||||||
return form_group
|
return form_group
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
/*
|
/*
|
||||||
* Clamped-width.
|
* Clamped-width.
|
||||||
* Usage:
|
* Usage:
|
||||||
* <div data-clampedwidth=".myParent">This long content will force clamped width</div>
|
* <div data-clampedwidth=".myParent">This long content will force clamped width</div>
|
||||||
*
|
*
|
||||||
* Author: LV
|
* Author: LV
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$('[data-clampedwidth]').each(function () {
|
$('[data-clampedwidth]').each(function () {
|
||||||
var elem = $(this);
|
var elem = $(this);
|
||||||
var parentPanel = elem.data('clampedwidth');
|
var parentPanel = elem.data('clampedwidth');
|
||||||
|
@ -113,52 +113,52 @@ $(document).ready(function(){
|
||||||
resizeFn();
|
resizeFn();
|
||||||
$(window).resize(resizeFn);
|
$(window).resize(resizeFn);
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#settings-form').on('click', '.' + Settings.ADD_ROW_BUTTON_CLASS, function(){
|
$('#settings-form').on('click', '.' + Settings.ADD_ROW_BUTTON_CLASS, function(){
|
||||||
addTableRow(this);
|
addTableRow(this);
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#settings-form').on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){
|
$('#settings-form').on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){
|
||||||
deleteTableRow(this);
|
deleteTableRow(this);
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#settings-form').on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){
|
$('#settings-form').on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){
|
||||||
moveTableRow(this, true);
|
moveTableRow(this, true);
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#settings-form').on('click', '.' + Settings.MOVE_DOWN_BUTTON_CLASS, function(){
|
$('#settings-form').on('click', '.' + Settings.MOVE_DOWN_BUTTON_CLASS, function(){
|
||||||
moveTableRow(this, false);
|
moveTableRow(this, false);
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#settings-form').on('keypress', 'table input', function(e){
|
$('#settings-form').on('keypress', 'table input', function(e){
|
||||||
if (e.keyCode == 13) {
|
if (e.keyCode == 13) {
|
||||||
// capture enter in table input
|
// capture enter in table input
|
||||||
// if we have a sibling next to us that has an input, jump to it, otherwise check if we have a glyphicon for add to click
|
// if we have a sibling next to us that has an input, jump to it, otherwise check if we have a glyphicon for add to click
|
||||||
sibling = $(this).parent('td').next();
|
sibling = $(this).parent('td').next();
|
||||||
|
|
||||||
if (sibling.hasClass(Settings.DATA_COL_CLASS)) {
|
if (sibling.hasClass(Settings.DATA_COL_CLASS)) {
|
||||||
// set focus to next input
|
// set focus to next input
|
||||||
sibling.find('input').focus()
|
sibling.find('input').focus()
|
||||||
} else if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) {
|
} else if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) {
|
||||||
sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click()
|
sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click()
|
||||||
|
|
||||||
// set focus to the first input in the new row
|
// set focus to the first input in the new row
|
||||||
$(this).closest('table').find('tr.inputs input:first').focus()
|
$(this).closest('table').find('tr.inputs input:first').focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#settings-form').on('change', '.' + Settings.TRIGGER_CHANGE_CLASS , function(){
|
$('#settings-form').on('change', '.' + Settings.TRIGGER_CHANGE_CLASS , function(){
|
||||||
// this input was changed, add the changed data attribute to it
|
// this input was changed, add the changed data attribute to it
|
||||||
$(this).attr('data-changed', true)
|
$(this).attr('data-changed', true)
|
||||||
|
|
||||||
badgeSidebarForDifferences($(this))
|
badgeSidebarForDifferences($(this))
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#advanced-toggle-button').click(function(){
|
$('#advanced-toggle-button').click(function(){
|
||||||
Settings.showAdvanced = !Settings.showAdvanced
|
Settings.showAdvanced = !Settings.showAdvanced
|
||||||
var advancedSelector = $('.' + Settings.ADVANCED_CLASS)
|
var advancedSelector = $('.' + Settings.ADVANCED_CLASS)
|
||||||
|
|
||||||
if (Settings.showAdvanced) {
|
if (Settings.showAdvanced) {
|
||||||
advancedSelector.show()
|
advancedSelector.show()
|
||||||
$(this).html("Hide advanced")
|
$(this).html("Hide advanced")
|
||||||
|
@ -166,48 +166,48 @@ $(document).ready(function(){
|
||||||
advancedSelector.hide()
|
advancedSelector.hide()
|
||||||
$(this).html("Show advanced")
|
$(this).html("Show advanced")
|
||||||
}
|
}
|
||||||
|
|
||||||
$(this).blur()
|
$(this).blur()
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#settings-form').on('click', '#choose-domain-btn', function(){
|
$('#settings-form').on('click', '#choose-domain-btn', function(){
|
||||||
chooseFromHighFidelityDomains($(this))
|
chooseFromHighFidelityDomains($(this))
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#settings-form').on('change', 'select', function(){
|
$('#settings-form').on('change', 'select', function(){
|
||||||
$("input[name='" + $(this).attr('data-hidden-input') + "']").val($(this).val()).change()
|
$("input[name='" + $(this).attr('data-hidden-input') + "']").val($(this).val()).change()
|
||||||
})
|
})
|
||||||
|
|
||||||
var panelsSource = $('#panels-template').html()
|
var panelsSource = $('#panels-template').html()
|
||||||
Settings.panelsTemplate = _.template(panelsSource)
|
Settings.panelsTemplate = _.template(panelsSource)
|
||||||
|
|
||||||
var sidebarTemplate = $('#list-group-template').html()
|
var sidebarTemplate = $('#list-group-template').html()
|
||||||
Settings.sidebarTemplate = _.template(sidebarTemplate)
|
Settings.sidebarTemplate = _.template(sidebarTemplate)
|
||||||
|
|
||||||
// $('body').scrollspy({ target: '#setup-sidebar'})
|
// $('body').scrollspy({ target: '#setup-sidebar'})
|
||||||
|
|
||||||
reloadSettings()
|
reloadSettings();
|
||||||
})
|
})
|
||||||
|
|
||||||
function reloadSettings() {
|
function reloadSettings() {
|
||||||
$.getJSON('/settings.json', function(data){
|
$.getJSON('/settings.json', function(data){
|
||||||
_.extend(data, viewHelpers)
|
_.extend(data, viewHelpers)
|
||||||
|
|
||||||
$('.nav-stacked').html(Settings.sidebarTemplate(data))
|
$('.nav-stacked').html(Settings.sidebarTemplate(data))
|
||||||
$('#panels').html(Settings.panelsTemplate(data))
|
$('#panels').html(Settings.panelsTemplate(data))
|
||||||
|
|
||||||
Settings.initialValues = form2js('settings-form', ".", false, cleanupFormValues, true);
|
Settings.initialValues = form2js('settings-form', ".", false, cleanupFormValues, true);
|
||||||
|
|
||||||
// add tooltip to locked settings
|
// add tooltip to locked settings
|
||||||
$('label.locked').tooltip({
|
$('label.locked').tooltip({
|
||||||
placement: 'right',
|
placement: 'right',
|
||||||
title: 'This setting is in the master config file and cannot be changed'
|
title: 'This setting is in the master config file and cannot be changed'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!_.has(data["locked"], "metaverse") && !_.has(data["locked"]["metaverse"], "id")) {
|
if (!_.has(data["locked"], "metaverse") && !_.has(data["locked"]["metaverse"], "id")) {
|
||||||
// append the domain selection modal, as long as it's not locked
|
// append the domain selection modal, as long as it's not locked
|
||||||
appendDomainSelectionModal()
|
appendDomainSelectionModal()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,20 +224,20 @@ $('body').on('click', '.save-button', function(e){
|
||||||
$("input:not([data-changed])").each(function(){
|
$("input:not([data-changed])").each(function(){
|
||||||
$(this).prop('disabled', true);
|
$(this).prop('disabled', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// grab a JSON representation of the form via form2js
|
// grab a JSON representation of the form via form2js
|
||||||
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
|
var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true);
|
||||||
|
|
||||||
console.log(formJSON);
|
console.log(formJSON);
|
||||||
|
|
||||||
// re-enable all inputs
|
// re-enable all inputs
|
||||||
$("input").each(function(){
|
$("input").each(function(){
|
||||||
$(this).prop('disabled', false);
|
$(this).prop('disabled', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// remove focus from the button
|
// remove focus from the button
|
||||||
$(this).blur()
|
$(this).blur()
|
||||||
|
|
||||||
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
// POST the form JSON to the domain-server settings.json endpoint so the settings are saved
|
||||||
$.ajax('/settings.json', {
|
$.ajax('/settings.json', {
|
||||||
data: JSON.stringify(formJSON),
|
data: JSON.stringify(formJSON),
|
||||||
|
@ -254,36 +254,41 @@ $('body').on('click', '.save-button', function(e){
|
||||||
showErrorMessage("Error", SETTINGS_ERROR_MESSAGE)
|
showErrorMessage("Error", SETTINGS_ERROR_MESSAGE)
|
||||||
reloadSettings();
|
reloadSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
function makeTable(setting, setting_name, setting_value, isLocked) {
|
function makeTable(setting, keypath, setting_value, isLocked) {
|
||||||
var isArray = !_.has(setting, 'key')
|
var isArray = !_.has(setting, 'key')
|
||||||
|
|
||||||
if (!isArray && setting.can_order) {
|
if (!isArray && setting.can_order) {
|
||||||
setting.can_order = false;
|
setting.can_order = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var html = "<span class='help-block'>" + setting.help + "</span>"
|
var html = "";
|
||||||
html += "<table class='table table-bordered " + (isLocked ? "locked-table" : "") + "' data-short-name='" + setting.name + "' name='" + setting_name
|
|
||||||
+ "' data-setting-type='" + (isArray ? 'array' : 'hash') + "'>"
|
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
|
// Column names
|
||||||
html += "<tr class='headers'>"
|
html += "<tr class='headers'>"
|
||||||
|
|
||||||
if (setting.numbered === true) {
|
if (setting.numbered === true) {
|
||||||
html += "<td class='number'><strong>#</strong></td>" // Row number
|
html += "<td class='number'><strong>#</strong></td>" // Row number
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setting.key) {
|
if (setting.key) {
|
||||||
html += "<td class='key'><strong>" + setting.key.label + "</strong></td>" // Key
|
html += "<td class='key'><strong>" + setting.key.label + "</strong></td>" // Key
|
||||||
}
|
}
|
||||||
|
|
||||||
_.each(setting.columns, function(col) {
|
_.each(setting.columns, function(col) {
|
||||||
html += "<td class='data'><strong>" + col.label + "</strong></td>" // Data
|
html += "<td class='data'><strong>" + col.label + "</strong></td>" // Data
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!isLocked) {
|
if (!isLocked) {
|
||||||
if (setting.can_order) {
|
if (setting.can_order) {
|
||||||
html += "<td class=" + Settings.REORDER_BUTTONS_CLASSES +
|
html += "<td class=" + Settings.REORDER_BUTTONS_CLASSES +
|
||||||
|
@ -291,39 +296,39 @@ function makeTable(setting, setting_name, setting_value, isLocked) {
|
||||||
}
|
}
|
||||||
html += "<td class=" + Settings.ADD_DEL_BUTTONS_CLASSES + "></td></tr>"
|
html += "<td class=" + Settings.ADD_DEL_BUTTONS_CLASSES + "></td></tr>"
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate rows in the table from existing values
|
// populate rows in the table from existing values
|
||||||
var row_num = 1
|
var row_num = 1
|
||||||
|
|
||||||
_.each(setting_value, function(row, indexOrName) {
|
_.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) {
|
if (setting.numbered === true) {
|
||||||
html += "<td class='numbered'>" + row_num + "</td>"
|
html += "<td class='numbered'>" + row_num + "</td>"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setting.key) {
|
if (setting.key) {
|
||||||
html += "<td class='key'>" + indexOrName + "</td>"
|
html += "<td class='key'>" + indexOrName + "</td>"
|
||||||
}
|
}
|
||||||
|
|
||||||
_.each(setting.columns, function(col) {
|
_.each(setting.columns, function(col) {
|
||||||
html += "<td class='" + Settings.DATA_COL_CLASS + "'>"
|
html += "<td class='" + Settings.DATA_COL_CLASS + "'>"
|
||||||
|
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
rowIsObject = setting.columns.length > 1
|
rowIsObject = setting.columns.length > 1
|
||||||
colValue = rowIsObject ? row[col.name] : row
|
colValue = rowIsObject ? row[col.name] : row
|
||||||
html += colValue
|
html += colValue
|
||||||
|
|
||||||
// for arrays we add a hidden input to this td so that values can be posted appropriately
|
// 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 + "'/>"
|
+ (rowIsObject ? "." + col.name : "") + "' value='" + colValue + "'/>"
|
||||||
} else if (row.hasOwnProperty(col.name)) {
|
} else if (row.hasOwnProperty(col.name)) {
|
||||||
html += row[col.name]
|
html += row[col.name]
|
||||||
}
|
}
|
||||||
|
|
||||||
html += "</td>"
|
html += "</td>"
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!isLocked) {
|
if (!isLocked) {
|
||||||
if (setting.can_order) {
|
if (setting.can_order) {
|
||||||
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES+
|
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES+
|
||||||
|
@ -333,86 +338,97 @@ function makeTable(setting, setting_name, setting_value, isLocked) {
|
||||||
html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES +
|
html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES +
|
||||||
"'><span class='" + Settings.DEL_ROW_SPAN_CLASSES + "'></span></td>"
|
"'><span class='" + Settings.DEL_ROW_SPAN_CLASSES + "'></span></td>"
|
||||||
}
|
}
|
||||||
|
|
||||||
html += "</tr>"
|
html += "</tr>"
|
||||||
|
|
||||||
row_num++
|
row_num++
|
||||||
})
|
})
|
||||||
|
|
||||||
// populate inputs in the table for new values
|
// populate inputs in the table for new values
|
||||||
if (!isLocked) {
|
if (!isLocked) {
|
||||||
html += makeTableInputs(setting)
|
html += makeTableInputs(setting)
|
||||||
}
|
}
|
||||||
html += "</table>"
|
html += "</table>"
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeTableInputs(setting) {
|
function makeTableInputs(setting) {
|
||||||
var html = "<tr class='inputs'>"
|
var html = "<tr class='inputs'>"
|
||||||
|
|
||||||
if (setting.numbered === true) {
|
if (setting.numbered === true) {
|
||||||
html += "<td class='numbered'></td>"
|
html += "<td class='numbered'></td>"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setting.key) {
|
if (setting.key) {
|
||||||
html += "<td class='key' name='" + setting.key.name + "'>\
|
html += "<td class='key' name='" + setting.key.name + "'>\
|
||||||
<input type='text' class='form-control' placeholder='" + (_.has(setting.key, 'placeholder') ? setting.key.placeholder : "") + "' value=''>\
|
<input type='text' class='form-control' placeholder='" + (_.has(setting.key, 'placeholder') ? setting.key.placeholder : "") + "' value=''>\
|
||||||
</td>"
|
</td>"
|
||||||
}
|
}
|
||||||
|
|
||||||
_.each(setting.columns, function(col) {
|
_.each(setting.columns, function(col) {
|
||||||
html += "<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>\
|
html += "<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>\
|
||||||
<input type='text' class='form-control' placeholder='" + (col.placeholder ? col.placeholder : "") + "'\
|
<input type='text' class='form-control' placeholder='" + (col.placeholder ? col.placeholder : "") + "'\
|
||||||
value='" + (col.default ? col.default : "") + "' data-default='" + (col.default ? col.default : "") + "'>\
|
value='" + (col.default ? col.default : "") + "' data-default='" + (col.default ? col.default : "") + "'>\
|
||||||
</td>"
|
</td>"
|
||||||
})
|
})
|
||||||
|
|
||||||
if (setting.can_order) {
|
if (setting.can_order) {
|
||||||
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES + "'></td>"
|
html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES + "'></td>"
|
||||||
}
|
}
|
||||||
html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES +
|
html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES +
|
||||||
"'><span class='glyphicon glyphicon-plus " + Settings.ADD_ROW_BUTTON_CLASS + "'></span></td>"
|
"'><span class='glyphicon glyphicon-plus " + Settings.ADD_ROW_BUTTON_CLASS + "'></span></td>"
|
||||||
html += "</tr>"
|
html += "</tr>"
|
||||||
|
|
||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
|
|
||||||
function badgeSidebarForDifferences(changedElement) {
|
function badgeSidebarForDifferences(changedElement) {
|
||||||
// figure out which group this input is in
|
// figure out which group this input is in
|
||||||
var panelParentID = changedElement.closest('.panel').attr('id')
|
var panelParentID = changedElement.closest('.panel').attr('id')
|
||||||
|
|
||||||
// get a JSON representation of that section
|
// if the panel contains non-grouped settings, the initial value is Settings.initialValues
|
||||||
var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID]
|
var isGrouped = $(panelParentID).hasClass('grouped');
|
||||||
var initialPanelJSON = Settings.initialValues[panelParentID]
|
|
||||||
|
if (isGrouped) {
|
||||||
|
var initialPanelJSON = Settings.initialValues[panelParentID];
|
||||||
|
|
||||||
|
// get a JSON representation of that section
|
||||||
|
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
|
var badgeValue = 0
|
||||||
|
|
||||||
// badge for any settings we have that are not the same or are not present in initialValues
|
// badge for any settings we have that are not the same or are not present in initialValues
|
||||||
for (var setting in panelJSON) {
|
for (var setting in panelJSON) {
|
||||||
if ((!_.has(initialPanelJSON, setting) && panelJSON[setting] !== "") ||
|
if ((!_.has(initialPanelJSON, setting) && panelJSON[setting] !== "") ||
|
||||||
(!_.isEqual(panelJSON[setting], initialPanelJSON[setting])
|
(!_.isEqual(panelJSON[setting], initialPanelJSON[setting])
|
||||||
&& (panelJSON[setting] !== "" || _.has(initialPanelJSON, setting)))) {
|
&& (panelJSON[setting] !== "" || _.has(initialPanelJSON, setting)))) {
|
||||||
badgeValue += 1
|
badgeValue += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the list-group-item badge to have the new value
|
// update the list-group-item badge to have the new value
|
||||||
if (badgeValue == 0) {
|
if (badgeValue == 0) {
|
||||||
badgeValue = ""
|
badgeValue = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
$("a[href='#" + panelParentID + "'] .badge").html(badgeValue);
|
$("a[href='#" + panelParentID + "'] .badge").html(badgeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTableRow(add_glyphicon) {
|
function addTableRow(add_glyphicon) {
|
||||||
var row = $(add_glyphicon).closest('tr')
|
var row = $(add_glyphicon).closest('tr')
|
||||||
|
|
||||||
var table = row.parents('table')
|
var table = row.parents('table')
|
||||||
var isArray = table.data('setting-type') === 'array'
|
var isArray = table.data('setting-type') === 'array'
|
||||||
|
|
||||||
var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS)
|
var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS)
|
||||||
|
|
||||||
if (!isArray) {
|
if (!isArray) {
|
||||||
// Check key spaces
|
// Check key spaces
|
||||||
var key = row.children(".key").children("input").val()
|
var key = row.children(".key").children("input").val()
|
||||||
|
@ -433,7 +449,7 @@ function addTableRow(add_glyphicon) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check empty fields
|
// Check empty fields
|
||||||
var empty = false;
|
var empty = false;
|
||||||
_.each(row.children('.' + Settings.DATA_COL_CLASS + ' input'), function(element) {
|
_.each(row.children('.' + Settings.DATA_COL_CLASS + ' input'), function(element) {
|
||||||
|
@ -442,23 +458,23 @@ function addTableRow(add_glyphicon) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (empty) {
|
if (empty) {
|
||||||
showErrorMessage("Error", "Empty field(s)")
|
showErrorMessage("Error", "Empty field(s)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var input_clone = row.clone()
|
var input_clone = row.clone()
|
||||||
|
|
||||||
// Change input row to data row
|
// Change input row to data row
|
||||||
var table = row.parents("table")
|
var table = row.parents("table")
|
||||||
var setting_name = table.attr("name")
|
var setting_name = table.attr("name")
|
||||||
var full_name = setting_name + "." + key
|
var full_name = setting_name + "." + key
|
||||||
row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS)
|
row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS)
|
||||||
row.removeClass("inputs")
|
row.removeClass("inputs")
|
||||||
|
|
||||||
_.each(row.children(), function(element) {
|
_.each(row.children(), function(element) {
|
||||||
if ($(element).hasClass("numbered")) {
|
if ($(element).hasClass("numbered")) {
|
||||||
// Index row
|
// Index row
|
||||||
var numbers = columns.children(".numbered")
|
var numbers = columns.children(".numbered")
|
||||||
if (numbers.length > 0) {
|
if (numbers.length > 0) {
|
||||||
|
@ -478,88 +494,88 @@ function addTableRow(add_glyphicon) {
|
||||||
var input = $(element).children("input")
|
var input = $(element).children("input")
|
||||||
$(element).html(input.val())
|
$(element).html(input.val())
|
||||||
input.remove()
|
input.remove()
|
||||||
} else if ($(element).hasClass(Settings.DATA_COL_CLASS)) {
|
} else if ($(element).hasClass(Settings.DATA_COL_CLASS)) {
|
||||||
// Hide inputs
|
// Hide inputs
|
||||||
var input = $(element).children("input")
|
var input = $(element).children("input")
|
||||||
input.attr("type", "hidden")
|
input.attr("type", "hidden")
|
||||||
|
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length
|
var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length
|
||||||
var key = $(element).attr('name')
|
var key = $(element).attr('name')
|
||||||
|
|
||||||
// are there multiple columns or just one?
|
// are there multiple columns or just one?
|
||||||
// with multiple we have an array of Objects, with one we have an array of whatever the value type is
|
// with multiple we have an array of Objects, with one we have an array of whatever the value type is
|
||||||
var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length
|
var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length
|
||||||
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
|
input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""))
|
||||||
} else {
|
} else {
|
||||||
input.attr("name", full_name + "." + $(element).attr("name"))
|
input.attr("name", full_name + "." + $(element).attr("name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
input.attr("data-changed", "true")
|
input.attr("data-changed", "true")
|
||||||
|
|
||||||
$(element).append(input.val())
|
$(element).append(input.val())
|
||||||
} else {
|
} else {
|
||||||
console.log("Unknown table element")
|
console.log("Unknown table element")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
input_clone.find('input').each(function(){
|
input_clone.find('input').each(function(){
|
||||||
$(this).val($(this).attr('data-default'));
|
$(this).val($(this).attr('data-default'));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isArray) {
|
if (isArray) {
|
||||||
updateDataChangedForSiblingRows(row, true)
|
updateDataChangedForSiblingRows(row, true)
|
||||||
|
|
||||||
// the addition of any table row should remove the empty-array-row
|
// the addition of any table row should remove the empty-array-row
|
||||||
row.siblings('.empty-array-row').remove()
|
row.siblings('.empty-array-row').remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
badgeSidebarForDifferences($(table))
|
badgeSidebarForDifferences($(table))
|
||||||
|
|
||||||
row.parent().append(input_clone)
|
row.parent().append(input_clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteTableRow(delete_glyphicon) {
|
function deleteTableRow(delete_glyphicon) {
|
||||||
var row = $(delete_glyphicon).closest('tr')
|
var row = $(delete_glyphicon).closest('tr')
|
||||||
|
|
||||||
var table = $(row).closest('table')
|
var table = $(row).closest('table')
|
||||||
var isArray = table.data('setting-type') === 'array'
|
var isArray = table.data('setting-type') === 'array'
|
||||||
|
|
||||||
row.empty();
|
row.empty();
|
||||||
|
|
||||||
if (!isArray) {
|
if (!isArray) {
|
||||||
row.html("<input type='hidden' class='form-control' name='"
|
row.html("<input type='hidden' class='form-control' name='"
|
||||||
+ row.attr('name') + "' data-changed='true' value=''>");
|
+ row.attr('name') + "' data-changed='true' value=''>");
|
||||||
} else {
|
} else {
|
||||||
if (table.find('.' + Settings.DATA_ROW_CLASS).length > 1) {
|
if (table.find('.' + Settings.DATA_ROW_CLASS).length > 1) {
|
||||||
updateDataChangedForSiblingRows(row)
|
updateDataChangedForSiblingRows(row)
|
||||||
|
|
||||||
// this isn't the last row - we can just remove it
|
// this isn't the last row - we can just remove it
|
||||||
row.remove()
|
row.remove()
|
||||||
} else {
|
} else {
|
||||||
// this is the last row, we can't remove it completely since we need to post an empty array
|
// this is the last row, we can't remove it completely since we need to post an empty array
|
||||||
|
|
||||||
row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS)
|
row.removeClass(Settings.DATA_ROW_CLASS).removeClass(Settings.NEW_ROW_CLASS)
|
||||||
row.addClass('empty-array-row')
|
row.addClass('empty-array-row')
|
||||||
|
|
||||||
row.html("<input type='hidden' class='form-control' name='" + table.attr("name").replace('[]', '')
|
row.html("<input type='hidden' class='form-control' name='" + table.attr("name").replace('[]', '')
|
||||||
+ "' data-changed='true' value=''>");
|
+ "' data-changed='true' value=''>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated
|
// we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated
|
||||||
badgeSidebarForDifferences($(table))
|
badgeSidebarForDifferences($(table))
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveTableRow(move_glyphicon, move_up) {
|
function moveTableRow(move_glyphicon, move_up) {
|
||||||
var row = $(move_glyphicon).closest('tr')
|
var row = $(move_glyphicon).closest('tr')
|
||||||
|
|
||||||
var table = $(row).closest('table')
|
var table = $(row).closest('table')
|
||||||
var isArray = table.data('setting-type') === 'array'
|
var isArray = table.data('setting-type') === 'array'
|
||||||
if (!isArray) {
|
if (!isArray) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (move_up) {
|
if (move_up) {
|
||||||
var prev_row = row.prev()
|
var prev_row = row.prev()
|
||||||
if (prev_row.hasClass(Settings.DATA_ROW_CLASS)) {
|
if (prev_row.hasClass(Settings.DATA_ROW_CLASS)) {
|
||||||
|
@ -571,7 +587,7 @@ function moveTableRow(move_glyphicon, move_up) {
|
||||||
next_row.after(row)
|
next_row.after(row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated
|
// we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated
|
||||||
badgeSidebarForDifferences($(table))
|
badgeSidebarForDifferences($(table))
|
||||||
}
|
}
|
||||||
|
@ -579,23 +595,23 @@ function moveTableRow(move_glyphicon, move_up) {
|
||||||
function updateDataChangedForSiblingRows(row, forceTrue) {
|
function updateDataChangedForSiblingRows(row, forceTrue) {
|
||||||
// anytime a new row is added to an array we need to set data-changed for all sibling row inputs to true
|
// anytime a new row is added to an array we need to set data-changed for all sibling row inputs to true
|
||||||
// unless it matches the inital set of values
|
// unless it matches the inital set of values
|
||||||
|
|
||||||
if (!forceTrue) {
|
if (!forceTrue) {
|
||||||
// figure out which group this row is in
|
// figure out which group this row is in
|
||||||
var panelParentID = row.closest('.panel').attr('id')
|
var panelParentID = row.closest('.panel').attr('id')
|
||||||
// get the short name for the setting from the table
|
// get the short name for the setting from the table
|
||||||
var tableShortName = row.closest('table').data('short-name')
|
var tableShortName = row.closest('table').data('short-name')
|
||||||
|
|
||||||
// get a JSON representation of that section
|
// get a JSON representation of that section
|
||||||
var panelSettingJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID][tableShortName]
|
var panelSettingJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID][tableShortName]
|
||||||
var initialPanelSettingJSON = Settings.initialValues[panelParentID][tableShortName]
|
var initialPanelSettingJSON = Settings.initialValues[panelParentID][tableShortName]
|
||||||
|
|
||||||
// if they are equal, we don't need data-changed
|
// if they are equal, we don't need data-changed
|
||||||
isTrue = !_.isEqual(panelSettingJSON, initialPanelSettingJSON)
|
isTrue = !_.isEqual(panelSettingJSON, initialPanelSettingJSON)
|
||||||
} else {
|
} else {
|
||||||
isTrue = true
|
isTrue = true
|
||||||
}
|
}
|
||||||
|
|
||||||
row.siblings('.' + Settings.DATA_ROW_CLASS).each(function(){
|
row.siblings('.' + Settings.DATA_ROW_CLASS).each(function(){
|
||||||
var hiddenInput = $(this).find('td.' + Settings.DATA_COL_CLASS + ' input')
|
var hiddenInput = $(this).find('td.' + Settings.DATA_COL_CLASS + ' input')
|
||||||
if (isTrue) {
|
if (isTrue) {
|
||||||
|
@ -611,19 +627,19 @@ function showRestartModal() {
|
||||||
backdrop: 'static',
|
backdrop: 'static',
|
||||||
keyboard: false
|
keyboard: false
|
||||||
});
|
});
|
||||||
|
|
||||||
var secondsElapsed = 0;
|
var secondsElapsed = 0;
|
||||||
var numberOfSecondsToWait = 3;
|
var numberOfSecondsToWait = 3;
|
||||||
|
|
||||||
var refreshSpan = $('span#refresh-time')
|
var refreshSpan = $('span#refresh-time')
|
||||||
refreshSpan.html(numberOfSecondsToWait + " seconds");
|
refreshSpan.html(numberOfSecondsToWait + " seconds");
|
||||||
|
|
||||||
// call ourselves every 1 second to countdown
|
// call ourselves every 1 second to countdown
|
||||||
var refreshCountdown = setInterval(function(){
|
var refreshCountdown = setInterval(function(){
|
||||||
secondsElapsed++;
|
secondsElapsed++;
|
||||||
secondsLeft = numberOfSecondsToWait - secondsElapsed
|
secondsLeft = numberOfSecondsToWait - secondsElapsed
|
||||||
refreshSpan.html(secondsLeft + (secondsLeft == 1 ? " second" : " seconds"))
|
refreshSpan.html(secondsLeft + (secondsLeft == 1 ? " second" : " seconds"))
|
||||||
|
|
||||||
if (secondsElapsed == numberOfSecondsToWait) {
|
if (secondsElapsed == numberOfSecondsToWait) {
|
||||||
location.reload(true);
|
location.reload(true);
|
||||||
clearInterval(refreshCountdown);
|
clearInterval(refreshCountdown);
|
||||||
|
@ -646,22 +662,22 @@ function showErrorMessage(title, message) {
|
||||||
function chooseFromHighFidelityDomains(clickedButton) {
|
function chooseFromHighFidelityDomains(clickedButton) {
|
||||||
// setup the modal to help user pick their domain
|
// setup the modal to help user pick their domain
|
||||||
if (Settings.initialValues.metaverse.access_token) {
|
if (Settings.initialValues.metaverse.access_token) {
|
||||||
|
|
||||||
// add a spinner to the choose button
|
// add a spinner to the choose button
|
||||||
clickedButton.html("Loading domains...")
|
clickedButton.html("Loading domains...")
|
||||||
clickedButton.attr('disabled', 'disabled')
|
clickedButton.attr('disabled', 'disabled')
|
||||||
|
|
||||||
// get a list of user domains from data-web
|
// get a list of user domains from data-web
|
||||||
data_web_domains_url = "https://metaverse.highfidelity.com/api/v1/domains?access_token="
|
data_web_domains_url = "https://metaverse.highfidelity.com/api/v1/domains?access_token="
|
||||||
$.getJSON(data_web_domains_url + Settings.initialValues.metaverse.access_token, function(data){
|
$.getJSON(data_web_domains_url + Settings.initialValues.metaverse.access_token, function(data){
|
||||||
|
|
||||||
modal_buttons = {
|
modal_buttons = {
|
||||||
cancel: {
|
cancel: {
|
||||||
label: 'Cancel',
|
label: 'Cancel',
|
||||||
className: 'btn-default'
|
className: 'btn-default'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.data.domains.length) {
|
if (data.data.domains.length) {
|
||||||
// setup a select box for the returned domains
|
// setup a select box for the returned domains
|
||||||
modal_body = "<p>Choose the High Fidelity domain you want this domain-server to represent.<br/>This will set your domain ID on the settings page.</p>"
|
modal_body = "<p>Choose the High Fidelity domain you want this domain-server to represent.<br/>This will set your domain ID on the settings page.</p>"
|
||||||
|
@ -685,25 +701,25 @@ function chooseFromHighFidelityDomains(clickedButton) {
|
||||||
window.open("https://metaverse.highfidelity.com/user/domains", '_blank');
|
window.open("https://metaverse.highfidelity.com/user/domains", '_blank');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
modal_body = "<p>You do not have any domains in your High Fidelity account." +
|
modal_body = "<p>You do not have any domains in your High Fidelity account." +
|
||||||
"<br/><br/>Go to your domains page to create a new one. Once your domain is created re-open this dialog to select it.</p>"
|
"<br/><br/>Go to your domains page to create a new one. Once your domain is created re-open this dialog to select it.</p>"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bootbox.dialog({
|
bootbox.dialog({
|
||||||
title: "Choose matching domain",
|
title: "Choose matching domain",
|
||||||
message: modal_body,
|
message: modal_body,
|
||||||
buttons: modal_buttons
|
buttons: modal_buttons
|
||||||
})
|
})
|
||||||
|
|
||||||
// remove the spinner from the choose button
|
// remove the spinner from the choose button
|
||||||
clickedButton.html("Choose from my domains")
|
clickedButton.html("Choose from my domains")
|
||||||
clickedButton.removeAttr('disabled')
|
clickedButton.removeAttr('disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
bootbox.alert({
|
bootbox.alert({
|
||||||
message: "You must have an access token to query your High Fidelity domains.<br><br>" +
|
message: "You must have an access token to query your High Fidelity domains.<br><br>" +
|
||||||
"Please follow the instructions on the settings page to add an access token.",
|
"Please follow the instructions on the settings page to add an access token.",
|
||||||
title: "Access token required"
|
title: "Access token required"
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,31 +5,32 @@
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="alert" style="display:none;"></div>
|
<div class="alert" style="display:none;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-3 col-sm-3" id="setup-sidebar-col">
|
<div class="col-md-3 col-sm-3" id="setup-sidebar-col">
|
||||||
<div id="setup-sidebar" data-clampedwidth="#setup-sidebar-col" class="hidden-xs" data-spy="affix" data-offset-top="55">
|
<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">
|
<script id="list-group-template" type="text/template">
|
||||||
<% _.each(descriptions, function(group){ %>
|
<% _.each(descriptions, function(group){ %>
|
||||||
|
<% panelID = group.name ? group.name : group.label %>
|
||||||
<li>
|
<li>
|
||||||
<a href="#<%-group.name %>" class="list-group-item">
|
<a href="#<%- panelID %>" class="list-group-item">
|
||||||
<span class="badge"></span>
|
<span class="badge"></span>
|
||||||
<%- group.label %>
|
<%- group.label %>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ul class="nav nav-pills nav-stacked">
|
<ul class="nav nav-pills nav-stacked">
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<button id="advanced-toggle-button" hidden=true class="btn btn-info">Show advanced</button>
|
<button id="advanced-toggle-button" hidden=true class="btn btn-info">Show advanced</button>
|
||||||
<button class="btn btn-success save-button">Save and restart</button>
|
<button class="btn btn-success save-button">Save and restart</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-9 col-sm-9 col-xs-12">
|
<div class="col-md-9 col-sm-9 col-xs-12">
|
||||||
<form id="settings-form" role="form">
|
<form id="settings-form" role="form">
|
||||||
|
|
||||||
<script id="panels-template" type="text/template">
|
<script id="panels-template" type="text/template">
|
||||||
<% _.each(descriptions, function(group){ %>
|
<% _.each(descriptions, function(group){ %>
|
||||||
<% split_settings = _.partition(group.settings, function(value, index) { return !value.advanced }) %>
|
<% split_settings = _.partition(group.settings, function(value, index) { return !value.advanced }) %>
|
||||||
|
@ -37,32 +38,40 @@
|
||||||
<% if (isAdvanced) { %>
|
<% if (isAdvanced) { %>
|
||||||
<% $("a[href=#" + group.name + "]").addClass('advanced-setting').hide() %>
|
<% $("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">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title"><%- group.label %></h3>
|
<h3 class="panel-title"><%- group.label %></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<% _.each(split_settings[0], function(setting) { %>
|
<% _.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))) %>
|
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
<% if (!_.isEmpty(split_settings[1])) { %>
|
<% if (!_.isEmpty(split_settings[1])) { %>
|
||||||
<% $("#advanced-toggle-button").show() %>
|
<% $("#advanced-toggle-button").show() %>
|
||||||
<% _.each(split_settings[1], function(setting) { %>
|
<% _.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))) %>
|
(_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
<% }%>
|
<% }%>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id="panels"></div>
|
<div id="panels"></div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xs-12 hidden-sm hidden-md hidden-lg">
|
<div class="col-xs-12 hidden-sm hidden-md hidden-lg">
|
||||||
<button class="btn btn-success save-button" id="small-save-button">Save and restart</button>
|
<button class="btn btn-success save-button" id="small-save-button">Save and restart</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,8 +92,9 @@
|
||||||
|
|
||||||
<!--#include virtual="footer.html"-->
|
<!--#include virtual="footer.html"-->
|
||||||
<script src='/js/underscore-min.js'></script>
|
<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/bootbox.min.js'></script>
|
||||||
<script src='/js/sweet-alert.min.js'></script>
|
<script src='/js/sweet-alert.min.js'></script>
|
||||||
<script src='/js/settings.js'></script>
|
<script src='/js/settings.js'></script>
|
||||||
<script src='/js/form2js.min.js'></script>
|
<script src='/js/form2js.min.js'></script>
|
||||||
<!--#include virtual="page-end.html"-->
|
<!--#include virtual="page-end.html"-->
|
||||||
|
|
|
@ -9,6 +9,6 @@
|
||||||
<script src='js/json.human.js'></script>
|
<script src='js/json.human.js'></script>
|
||||||
<script src='js/highcharts-custom.js'></script>
|
<script src='js/highcharts-custom.js'></script>
|
||||||
<script src='/js/underscore-min.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>
|
<script src='/js/bootbox.min.js'></script>
|
||||||
<!--#include virtual="page-end.html"-->
|
<!--#include virtual="page-end.html"-->
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -39,29 +39,29 @@ class DomainServer : public QCoreApplication, public HTTPSRequestHandler {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
DomainServer(int argc, char* argv[]);
|
DomainServer(int argc, char* argv[]);
|
||||||
|
|
||||||
static int const EXIT_CODE_REBOOT;
|
static int const EXIT_CODE_REBOOT;
|
||||||
|
|
||||||
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
|
bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false);
|
||||||
bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false);
|
bool handleHTTPSRequest(HTTPSConnection* connection, const QUrl& url, bool skipSubHandler = false);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/// Called by NodeList to inform us a node has been added
|
/// Called by NodeList to inform us a node has been added
|
||||||
void nodeAdded(SharedNodePointer node);
|
void nodeAdded(SharedNodePointer node);
|
||||||
/// Called by NodeList to inform us a node has been killed
|
/// Called by NodeList to inform us a node has been killed
|
||||||
void nodeKilled(SharedNodePointer node);
|
void nodeKilled(SharedNodePointer node);
|
||||||
|
|
||||||
void publicKeyJSONCallback(QNetworkReply& requestReply);
|
void publicKeyJSONCallback(QNetworkReply& requestReply);
|
||||||
void transactionJSONCallback(const QJsonObject& data);
|
void transactionJSONCallback(const QJsonObject& data);
|
||||||
|
|
||||||
void restart();
|
void restart();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void loginFailed();
|
void loginFailed();
|
||||||
void readAvailableDatagrams();
|
void readAvailableDatagrams();
|
||||||
void setupPendingAssignmentCredits();
|
void setupPendingAssignmentCredits();
|
||||||
void sendPendingTransactionsToServer();
|
void sendPendingTransactionsToServer();
|
||||||
|
|
||||||
void requestCurrentPublicSocketViaSTUN();
|
void requestCurrentPublicSocketViaSTUN();
|
||||||
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr);
|
||||||
void performICEUpdates();
|
void performICEUpdates();
|
||||||
|
@ -74,23 +74,23 @@ private:
|
||||||
bool optionallyReadX509KeyAndCertificate();
|
bool optionallyReadX509KeyAndCertificate();
|
||||||
bool didSetupAccountManagerWithAccessToken();
|
bool didSetupAccountManagerWithAccessToken();
|
||||||
bool optionallySetupAssignmentPayment();
|
bool optionallySetupAssignmentPayment();
|
||||||
|
|
||||||
void setupAutomaticNetworking();
|
void setupAutomaticNetworking();
|
||||||
void sendHeartbeatToDataServer(const QString& networkAddress);
|
void sendHeartbeatToDataServer(const QString& networkAddress);
|
||||||
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||||
void processICEHeartbeatResponse(const QByteArray& packet);
|
void processICEHeartbeatResponse(const QByteArray& packet);
|
||||||
|
|
||||||
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||||
|
|
||||||
void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr);
|
||||||
unsigned int countConnectedUsers();
|
unsigned int countConnectedUsers();
|
||||||
bool verifyUsersKey (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn);
|
bool verifyUsersKey (const QString& username, const QByteArray& usernameSignature, QString& reasonReturn);
|
||||||
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
|
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
|
||||||
const HifiSockAddr& senderSockAddr, QString& reasonReturn);
|
const HifiSockAddr& senderSockAddr, QString& reasonReturn);
|
||||||
|
|
||||||
void preloadAllowedUserPublicKeys();
|
void preloadAllowedUserPublicKeys();
|
||||||
void requestUserPublicKey(const QString& username);
|
void requestUserPublicKey(const QString& username);
|
||||||
|
|
||||||
int parseNodeDataFromByteArray(QDataStream& packetStream,
|
int parseNodeDataFromByteArray(QDataStream& packetStream,
|
||||||
NodeType_t& nodeType,
|
NodeType_t& nodeType,
|
||||||
HifiSockAddr& publicSockAddr,
|
HifiSockAddr& publicSockAddr,
|
||||||
|
@ -99,61 +99,63 @@ private:
|
||||||
NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes);
|
NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes);
|
||||||
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr,
|
void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr,
|
||||||
const NodeSet& nodeInterestList);
|
const NodeSet& nodeInterestList);
|
||||||
|
|
||||||
void parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes);
|
void parseAssignmentConfigs(QSet<Assignment::Type>& excludedTypes);
|
||||||
void addStaticAssignmentToAssignmentHash(Assignment* newAssignment);
|
void addStaticAssignmentToAssignmentHash(Assignment* newAssignment);
|
||||||
void createStaticAssignmentsForType(Assignment::Type type, const QVariantList& configList);
|
void createStaticAssignmentsForType(Assignment::Type type, const QVariantList& configList);
|
||||||
void populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes);
|
void populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes);
|
||||||
void populateStaticScriptedAssignmentsFromSettings();
|
void populateStaticScriptedAssignmentsFromSettings();
|
||||||
|
|
||||||
SharedAssignmentPointer matchingQueuedAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType);
|
SharedAssignmentPointer matchingQueuedAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType);
|
||||||
SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment);
|
SharedAssignmentPointer deployableAssignmentForRequest(const Assignment& requestAssignment);
|
||||||
void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment);
|
void removeMatchingAssignmentFromQueue(const SharedAssignmentPointer& removableAssignment);
|
||||||
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
|
void refreshStaticAssignmentAndAddToQueue(SharedAssignmentPointer& assignment);
|
||||||
void addStaticAssignmentsToQueue();
|
void addStaticAssignmentsToQueue();
|
||||||
|
|
||||||
|
void respondToPathQuery(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||||
|
|
||||||
QUrl oauthRedirectURL();
|
QUrl oauthRedirectURL();
|
||||||
QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid());
|
QUrl oauthAuthorizationURL(const QUuid& stateUUID = QUuid::createUuid());
|
||||||
|
|
||||||
bool isAuthenticatedRequest(HTTPConnection* connection, const QUrl& url);
|
bool isAuthenticatedRequest(HTTPConnection* connection, const QUrl& url);
|
||||||
|
|
||||||
void handleTokenRequestFinished();
|
void handleTokenRequestFinished();
|
||||||
QNetworkReply* profileRequestGivenTokenReply(QNetworkReply* tokenReply);
|
QNetworkReply* profileRequestGivenTokenReply(QNetworkReply* tokenReply);
|
||||||
void handleProfileRequestFinished();
|
void handleProfileRequestFinished();
|
||||||
Headers setupCookieHeadersFromProfileReply(QNetworkReply* profileReply);
|
Headers setupCookieHeadersFromProfileReply(QNetworkReply* profileReply);
|
||||||
|
|
||||||
void loadExistingSessionsFromSettings();
|
void loadExistingSessionsFromSettings();
|
||||||
|
|
||||||
QJsonObject jsonForSocket(const HifiSockAddr& socket);
|
QJsonObject jsonForSocket(const HifiSockAddr& socket);
|
||||||
QJsonObject jsonObjectForNode(const SharedNodePointer& node);
|
QJsonObject jsonObjectForNode(const SharedNodePointer& node);
|
||||||
|
|
||||||
HTTPManager _httpManager;
|
HTTPManager _httpManager;
|
||||||
HTTPSManager* _httpsManager;
|
HTTPSManager* _httpsManager;
|
||||||
|
|
||||||
QHash<QUuid, SharedAssignmentPointer> _allAssignments;
|
QHash<QUuid, SharedAssignmentPointer> _allAssignments;
|
||||||
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
|
QQueue<SharedAssignmentPointer> _unfulfilledAssignments;
|
||||||
QHash<QUuid, PendingAssignedNodeData*> _pendingAssignedNodes;
|
QHash<QUuid, PendingAssignedNodeData*> _pendingAssignedNodes;
|
||||||
TransactionHash _pendingAssignmentCredits;
|
TransactionHash _pendingAssignmentCredits;
|
||||||
|
|
||||||
bool _isUsingDTLS;
|
bool _isUsingDTLS;
|
||||||
|
|
||||||
QUrl _oauthProviderURL;
|
QUrl _oauthProviderURL;
|
||||||
QString _oauthClientID;
|
QString _oauthClientID;
|
||||||
QString _oauthClientSecret;
|
QString _oauthClientSecret;
|
||||||
QString _hostname;
|
QString _hostname;
|
||||||
|
|
||||||
QSet<QUuid> _webAuthenticationStateSet;
|
QSet<QUuid> _webAuthenticationStateSet;
|
||||||
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
|
QHash<QUuid, DomainServerWebSessionData> _cookieSessionHash;
|
||||||
|
|
||||||
QHash<QString, QByteArray> _userPublicKeys;
|
QHash<QString, QByteArray> _userPublicKeys;
|
||||||
|
|
||||||
QHash<QUuid, NetworkPeer> _connectingICEPeers;
|
QHash<QUuid, NetworkPeer> _connectingICEPeers;
|
||||||
QHash<QUuid, HifiSockAddr> _connectedICEPeers;
|
QHash<QUuid, HifiSockAddr> _connectedICEPeers;
|
||||||
|
|
||||||
QString _automaticNetworkingSetting;
|
QString _automaticNetworkingSetting;
|
||||||
|
|
||||||
DomainServerSettingsManager _settingsManager;
|
DomainServerSettingsManager _settingsManager;
|
||||||
|
|
||||||
HifiSockAddr _iceServerSocket;
|
HifiSockAddr _iceServerSocket;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ const QString DESCRIPTION_NAME_KEY = "name";
|
||||||
const QString SETTING_DESCRIPTION_TYPE_KEY = "type";
|
const QString SETTING_DESCRIPTION_TYPE_KEY = "type";
|
||||||
const QString DESCRIPTION_COLUMNS_KEY = "columns";
|
const QString DESCRIPTION_COLUMNS_KEY = "columns";
|
||||||
|
|
||||||
|
const QString SETTINGS_VIEWPOINT_KEY = "viewpoint";
|
||||||
|
|
||||||
DomainServerSettingsManager::DomainServerSettingsManager() :
|
DomainServerSettingsManager::DomainServerSettingsManager() :
|
||||||
_descriptionArray(),
|
_descriptionArray(),
|
||||||
_configMap()
|
_configMap()
|
||||||
|
@ -39,31 +41,31 @@ DomainServerSettingsManager::DomainServerSettingsManager() :
|
||||||
// load the description object from the settings description
|
// load the description object from the settings description
|
||||||
QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH);
|
QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH);
|
||||||
descriptionFile.open(QIODevice::ReadOnly);
|
descriptionFile.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
_descriptionArray = QJsonDocument::fromJson(descriptionFile.readAll()).array();
|
_descriptionArray = QJsonDocument::fromJson(descriptionFile.readAll()).array();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList) {
|
||||||
_configMap.loadMasterAndUserConfig(argumentList);
|
_configMap.loadMasterAndUserConfig(argumentList);
|
||||||
|
|
||||||
// for now we perform a temporary transition from http-username and http-password to http_username and http_password
|
// for now we perform a temporary transition from http-username and http-password to http_username and http_password
|
||||||
const QVariant* oldUsername = valueForKeyPath(_configMap.getUserConfig(), "security.http-username");
|
const QVariant* oldUsername = valueForKeyPath(_configMap.getUserConfig(), "security.http-username");
|
||||||
const QVariant* oldPassword = valueForKeyPath(_configMap.getUserConfig(), "security.http-password");
|
const QVariant* oldPassword = valueForKeyPath(_configMap.getUserConfig(), "security.http-password");
|
||||||
|
|
||||||
if (oldUsername || oldPassword) {
|
if (oldUsername || oldPassword) {
|
||||||
QVariantMap& settingsMap = *reinterpret_cast<QVariantMap*>(_configMap.getUserConfig()["security"].data());
|
QVariantMap& settingsMap = *reinterpret_cast<QVariantMap*>(_configMap.getUserConfig()["security"].data());
|
||||||
|
|
||||||
// remove old keys, move to new format
|
// remove old keys, move to new format
|
||||||
if (oldUsername) {
|
if (oldUsername) {
|
||||||
settingsMap["http_username"] = oldUsername->toString();
|
settingsMap["http_username"] = oldUsername->toString();
|
||||||
settingsMap.remove("http-username");
|
settingsMap.remove("http-username");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldPassword) {
|
if (oldPassword) {
|
||||||
settingsMap["http_password"] = oldPassword->toString();
|
settingsMap["http_password"] = oldPassword->toString();
|
||||||
settingsMap.remove("http-password");
|
settingsMap.remove("http-password");
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the updated settings
|
// save the updated settings
|
||||||
persistToFile();
|
persistToFile();
|
||||||
}
|
}
|
||||||
|
@ -71,18 +73,18 @@ void DomainServerSettingsManager::setupConfigMap(const QStringList& argumentList
|
||||||
|
|
||||||
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString &keyPath) {
|
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString &keyPath) {
|
||||||
const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath);
|
const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath);
|
||||||
|
|
||||||
if (foundValue) {
|
if (foundValue) {
|
||||||
return *foundValue;
|
return *foundValue;
|
||||||
} else {
|
} else {
|
||||||
int dotIndex = keyPath.indexOf('.');
|
int dotIndex = keyPath.indexOf('.');
|
||||||
|
|
||||||
QString groupKey = keyPath.mid(0, dotIndex);
|
QString groupKey = keyPath.mid(0, dotIndex);
|
||||||
QString settingKey = keyPath.mid(dotIndex + 1);
|
QString settingKey = keyPath.mid(dotIndex + 1);
|
||||||
|
|
||||||
foreach(const QVariant& group, _descriptionArray.toVariantList()) {
|
foreach(const QVariant& group, _descriptionArray.toVariantList()) {
|
||||||
QVariantMap groupMap = group.toMap();
|
QVariantMap groupMap = group.toMap();
|
||||||
|
|
||||||
if (groupMap[DESCRIPTION_NAME_KEY].toString() == groupKey) {
|
if (groupMap[DESCRIPTION_NAME_KEY].toString() == groupKey) {
|
||||||
foreach(const QVariant& setting, groupMap[DESCRIPTION_SETTINGS_KEY].toList()) {
|
foreach(const QVariant& setting, groupMap[DESCRIPTION_SETTINGS_KEY].toList()) {
|
||||||
QVariantMap settingMap = setting.toMap();
|
QVariantMap settingMap = setting.toMap();
|
||||||
|
@ -90,12 +92,12 @@ QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QStrin
|
||||||
return settingMap[SETTING_DEFAULT_KEY];
|
return settingMap[SETTING_DEFAULT_KEY];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,23 +106,23 @@ const QString SETTINGS_PATH = "/settings.json";
|
||||||
bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) {
|
bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) {
|
||||||
if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH) {
|
if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH) {
|
||||||
// this is a GET operation for our settings
|
// this is a GET operation for our settings
|
||||||
|
|
||||||
// check if there is a query parameter for settings affecting a particular type of assignment
|
// check if there is a query parameter for settings affecting a particular type of assignment
|
||||||
const QString SETTINGS_TYPE_QUERY_KEY = "type";
|
const QString SETTINGS_TYPE_QUERY_KEY = "type";
|
||||||
QUrlQuery settingsQuery(url);
|
QUrlQuery settingsQuery(url);
|
||||||
QString typeValue = settingsQuery.queryItemValue(SETTINGS_TYPE_QUERY_KEY);
|
QString typeValue = settingsQuery.queryItemValue(SETTINGS_TYPE_QUERY_KEY);
|
||||||
|
|
||||||
if (!typeValue.isEmpty()) {
|
if (!typeValue.isEmpty()) {
|
||||||
QJsonObject responseObject = responseObjectForType(typeValue);
|
QJsonObject responseObject = responseObjectForType(typeValue);
|
||||||
|
|
||||||
connection->respond(HTTPConnection::StatusCode200, QJsonDocument(responseObject).toJson(), "application/json");
|
connection->respond(HTTPConnection::StatusCode200, QJsonDocument(responseObject).toJson(), "application/json");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,130 +131,125 @@ bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection
|
||||||
// this is a POST operation to change one or more settings
|
// this is a POST operation to change one or more settings
|
||||||
QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent());
|
QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent());
|
||||||
QJsonObject postedObject = postedDocument.object();
|
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
|
// 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
|
// store whatever the current _settingsMap is to file
|
||||||
persistToFile();
|
persistToFile();
|
||||||
|
|
||||||
// return success to the caller
|
// return success to the caller
|
||||||
QString jsonSuccess = "{\"status\": \"success\"}";
|
QString jsonSuccess = "{\"status\": \"success\"}";
|
||||||
connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json");
|
connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json");
|
||||||
|
|
||||||
// defer a restart to the domain-server, this gives our HTTPConnection enough time to respond
|
// defer a restart to the domain-server, this gives our HTTPConnection enough time to respond
|
||||||
const int DOMAIN_SERVER_RESTART_TIMER_MSECS = 1000;
|
const int DOMAIN_SERVER_RESTART_TIMER_MSECS = 1000;
|
||||||
QTimer::singleShot(DOMAIN_SERVER_RESTART_TIMER_MSECS, qApp, SLOT(restart()));
|
QTimer::singleShot(DOMAIN_SERVER_RESTART_TIMER_MSECS, qApp, SLOT(restart()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH) {
|
} else if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == SETTINGS_PATH) {
|
||||||
// setup a JSON Object with descriptions and non-omitted settings
|
// setup a JSON Object with descriptions and non-omitted settings
|
||||||
const QString SETTINGS_RESPONSE_DESCRIPTION_KEY = "descriptions";
|
const QString SETTINGS_RESPONSE_DESCRIPTION_KEY = "descriptions";
|
||||||
const QString SETTINGS_RESPONSE_VALUE_KEY = "values";
|
const QString SETTINGS_RESPONSE_VALUE_KEY = "values";
|
||||||
const QString SETTINGS_RESPONSE_LOCKED_VALUES_KEY = "locked";
|
const QString SETTINGS_RESPONSE_LOCKED_VALUES_KEY = "locked";
|
||||||
|
|
||||||
QJsonObject rootObject;
|
QJsonObject rootObject;
|
||||||
rootObject[SETTINGS_RESPONSE_DESCRIPTION_KEY] = _descriptionArray;
|
rootObject[SETTINGS_RESPONSE_DESCRIPTION_KEY] = _descriptionArray;
|
||||||
rootObject[SETTINGS_RESPONSE_VALUE_KEY] = responseObjectForType("", true);
|
rootObject[SETTINGS_RESPONSE_VALUE_KEY] = responseObjectForType("", true);
|
||||||
rootObject[SETTINGS_RESPONSE_LOCKED_VALUES_KEY] = QJsonDocument::fromVariant(_configMap.getMasterConfig()).object();
|
rootObject[SETTINGS_RESPONSE_LOCKED_VALUES_KEY] = QJsonDocument::fromVariant(_configMap.getMasterConfig()).object();
|
||||||
|
|
||||||
|
|
||||||
connection->respond(HTTPConnection::StatusCode200, QJsonDocument(rootObject).toJson(), "application/json");
|
connection->respond(HTTPConnection::StatusCode200, QJsonDocument(rootObject).toJson(), "application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& typeValue, bool isAuthenticated) {
|
QJsonObject DomainServerSettingsManager::responseObjectForType(const QString& typeValue, bool isAuthenticated) {
|
||||||
QJsonObject responseObject;
|
QJsonObject responseObject;
|
||||||
|
|
||||||
if (!typeValue.isEmpty() || isAuthenticated) {
|
if (!typeValue.isEmpty() || isAuthenticated) {
|
||||||
// convert the string type value to a QJsonValue
|
// convert the string type value to a QJsonValue
|
||||||
QJsonValue queryType = typeValue.isEmpty() ? QJsonValue() : QJsonValue(typeValue.toInt());
|
QJsonValue queryType = typeValue.isEmpty() ? QJsonValue() : QJsonValue(typeValue.toInt());
|
||||||
|
|
||||||
const QString AFFECTED_TYPES_JSON_KEY = "assignment-types";
|
const QString AFFECTED_TYPES_JSON_KEY = "assignment-types";
|
||||||
|
|
||||||
// enumerate the groups in the description object to find which settings to pass
|
// enumerate the groups in the description object to find which settings to pass
|
||||||
foreach(const QJsonValue& groupValue, _descriptionArray) {
|
foreach(const QJsonValue& groupValue, _descriptionArray) {
|
||||||
QJsonObject groupObject = groupValue.toObject();
|
QJsonObject groupObject = groupValue.toObject();
|
||||||
QString groupKey = groupObject[DESCRIPTION_NAME_KEY].toString();
|
QString groupKey = groupObject[DESCRIPTION_NAME_KEY].toString();
|
||||||
QJsonArray groupSettingsArray = groupObject[DESCRIPTION_SETTINGS_KEY].toArray();
|
QJsonArray groupSettingsArray = groupObject[DESCRIPTION_SETTINGS_KEY].toArray();
|
||||||
|
|
||||||
QJsonObject groupResponseObject;
|
QJsonObject groupResponseObject;
|
||||||
|
|
||||||
foreach(const QJsonValue& settingValue, groupSettingsArray) {
|
foreach(const QJsonValue& settingValue, groupSettingsArray) {
|
||||||
const QString VALUE_HIDDEN_FLAG_KEY = "value-hidden";
|
const QString VALUE_HIDDEN_FLAG_KEY = "value-hidden";
|
||||||
|
|
||||||
QJsonObject settingObject = settingValue.toObject();
|
QJsonObject settingObject = settingValue.toObject();
|
||||||
|
|
||||||
if (!settingObject[VALUE_HIDDEN_FLAG_KEY].toBool()) {
|
if (!settingObject[VALUE_HIDDEN_FLAG_KEY].toBool()) {
|
||||||
QJsonArray affectedTypesArray = settingObject[AFFECTED_TYPES_JSON_KEY].toArray();
|
QJsonArray affectedTypesArray = settingObject[AFFECTED_TYPES_JSON_KEY].toArray();
|
||||||
if (affectedTypesArray.isEmpty()) {
|
if (affectedTypesArray.isEmpty()) {
|
||||||
affectedTypesArray = groupObject[AFFECTED_TYPES_JSON_KEY].toArray();
|
affectedTypesArray = groupObject[AFFECTED_TYPES_JSON_KEY].toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (affectedTypesArray.contains(queryType) ||
|
if (affectedTypesArray.contains(queryType) ||
|
||||||
(queryType.isNull() && isAuthenticated)) {
|
(queryType.isNull() && isAuthenticated)) {
|
||||||
// this is a setting we should include in the responseObject
|
// this is a setting we should include in the responseObject
|
||||||
|
|
||||||
QString settingName = settingObject[DESCRIPTION_NAME_KEY].toString();
|
QString settingName = settingObject[DESCRIPTION_NAME_KEY].toString();
|
||||||
|
|
||||||
// we need to check if the settings map has a value for this setting
|
// we need to check if the settings map has a value for this setting
|
||||||
QVariant variantValue;
|
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);
|
if (!settingsMapGroupValue.isNull()) {
|
||||||
|
variantValue = settingsMapGroupValue.toMap().value(settingName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
variantValue = _configMap.getMergedConfig().value(settingName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonValue result;
|
||||||
|
|
||||||
if (variantValue.isNull()) {
|
if (variantValue.isNull()) {
|
||||||
// no value for this setting, pass the default
|
// no value for this setting, pass the default
|
||||||
if (settingObject.contains(SETTING_DEFAULT_KEY)) {
|
if (settingObject.contains(SETTING_DEFAULT_KEY)) {
|
||||||
groupResponseObject[settingName] = settingObject[SETTING_DEFAULT_KEY];
|
result = settingObject[SETTING_DEFAULT_KEY];
|
||||||
} else {
|
} else {
|
||||||
// users are allowed not to provide a default for string values
|
// users are allowed not to provide a default for string values
|
||||||
// if so we set to the empty string
|
// if so we set to the empty string
|
||||||
groupResponseObject[settingName] = QString("");
|
result = QString("");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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
|
// set this group's object to the constructed object
|
||||||
responseObject[groupKey] = groupResponseObject;
|
responseObject[groupKey] = groupResponseObject;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return responseObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DomainServerSettingsManager::settingExists(const QString& groupName, const QString& settingName,
|
|
||||||
const QJsonArray& descriptionArray, QJsonObject& settingDescription) {
|
return responseObject;
|
||||||
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,
|
void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
|
||||||
|
@ -266,13 +263,21 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV
|
||||||
QString settingType = settingDescription[SETTING_DESCRIPTION_TYPE_KEY].toString();
|
QString settingType = settingDescription[SETTING_DESCRIPTION_TYPE_KEY].toString();
|
||||||
const QString INPUT_DOUBLE_TYPE = "double";
|
const QString INPUT_DOUBLE_TYPE = "double";
|
||||||
const QString INPUT_INTEGER_TYPE = "int";
|
const QString INPUT_INTEGER_TYPE = "int";
|
||||||
|
|
||||||
if (settingType == INPUT_DOUBLE_TYPE) {
|
if (settingType == INPUT_DOUBLE_TYPE) {
|
||||||
settingMap[key] = newValue.toString().toDouble();
|
settingMap[key] = newValue.toString().toDouble();
|
||||||
} else if (settingType == INPUT_INTEGER_TYPE) {
|
} else if (settingType == INPUT_INTEGER_TYPE) {
|
||||||
settingMap[key] = newValue.toString().toInt();
|
settingMap[key] = newValue.toString().toInt();
|
||||||
} else {
|
} 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()) {
|
} else if (newValue.isBool()) {
|
||||||
|
@ -282,12 +287,12 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV
|
||||||
// we don't have a map below this key yet, so set it up now
|
// we don't have a map below this key yet, so set it up now
|
||||||
settingMap[key] = QVariantMap();
|
settingMap[key] = QVariantMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap& thisMap = *reinterpret_cast<QVariantMap*>(settingMap[key].data());
|
QVariantMap& thisMap = *reinterpret_cast<QVariantMap*>(settingMap[key].data());
|
||||||
foreach(const QString childKey, newValue.toObject().keys()) {
|
foreach(const QString childKey, newValue.toObject().keys()) {
|
||||||
|
|
||||||
QJsonObject childDescriptionObject = settingDescription;
|
QJsonObject childDescriptionObject = settingDescription;
|
||||||
|
|
||||||
// is this the key? if so we have the description already
|
// is this the key? if so we have the description already
|
||||||
if (key != settingDescription[DESCRIPTION_NAME_KEY].toString()) {
|
if (key != settingDescription[DESCRIPTION_NAME_KEY].toString()) {
|
||||||
// otherwise find the description object for this childKey under columns
|
// otherwise find the description object for this childKey under columns
|
||||||
|
@ -299,12 +304,20 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
if (settingMap[key].toMap().isEmpty()) {
|
||||||
// we've cleared all of the settings below this value, so remove this one too
|
// we've cleared all of the settings below this value, so remove this one too
|
||||||
settingMap.remove(key);
|
settingMap.remove(key);
|
||||||
|
@ -316,47 +329,104 @@ void DomainServerSettingsManager::updateSetting(const QString& key, const QJsonV
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject,
|
QJsonObject DomainServerSettingsManager::settingDescriptionFromGroup(const QJsonObject& groupObject, const QString& settingName) {
|
||||||
QVariantMap& settingsVariant,
|
foreach(const QJsonValue& settingValue, groupObject[DESCRIPTION_SETTINGS_KEY].toArray()) {
|
||||||
const QJsonArray& descriptionArray) {
|
QJsonObject settingObject = settingValue.toObject();
|
||||||
// Iterate on the setting groups
|
if (settingObject[DESCRIPTION_NAME_KEY].toString() == settingName) {
|
||||||
foreach(const QString& groupKey, postedObject.keys()) {
|
return settingObject;
|
||||||
QJsonValue groupValue = postedObject[groupKey];
|
|
||||||
|
|
||||||
if (!settingsVariant.contains(groupKey)) {
|
|
||||||
// we don't have a map below this key yet, so set it up now
|
|
||||||
settingsVariant[groupKey] = QVariantMap();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Iterate on the settings
|
|
||||||
foreach(const QString& settingKey, groupValue.toObject().keys()) {
|
return QJsonObject();
|
||||||
QJsonValue settingValue = groupValue.toObject()[settingKey];
|
}
|
||||||
|
|
||||||
QJsonObject thisDescription;
|
void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject,
|
||||||
if (settingExists(groupKey, settingKey, descriptionArray, thisDescription)) {
|
QVariantMap& settingsVariant) {
|
||||||
QVariantMap& thisMap = *reinterpret_cast<QVariantMap*>(settingsVariant[groupKey].data());
|
// Iterate on the setting groups
|
||||||
updateSetting(settingKey, settingValue, thisMap, thisDescription);
|
foreach(const QString& rootKey, postedObject.keys()) {
|
||||||
|
QJsonValue rootValue = postedObject[rootKey];
|
||||||
|
|
||||||
|
if (!settingsVariant.contains(rootKey)) {
|
||||||
|
// we don't have a map below this key yet, so set it up now
|
||||||
|
settingsVariant[rootKey] = QVariantMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap& thisMap = settingsVariant;
|
||||||
|
|
||||||
|
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
|
// we've cleared all of the settings below this value, so remove this one too
|
||||||
settingsVariant.remove(groupKey);
|
settingsVariant.remove(rootKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainServerSettingsManager::persistToFile() {
|
void DomainServerSettingsManager::persistToFile() {
|
||||||
|
|
||||||
// make sure we have the dir the settings file is supposed to live in
|
// make sure we have the dir the settings file is supposed to live in
|
||||||
QFileInfo settingsFileInfo(_configMap.getUserConfigFilename());
|
QFileInfo settingsFileInfo(_configMap.getUserConfigFilename());
|
||||||
|
|
||||||
if (!settingsFileInfo.dir().exists()) {
|
if (!settingsFileInfo.dir().exists()) {
|
||||||
settingsFileInfo.dir().mkpath(".");
|
settingsFileInfo.dir().mkpath(".");
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile settingsFile(_configMap.getUserConfigFilename());
|
QFile settingsFile(_configMap.getUserConfigFilename());
|
||||||
|
|
||||||
if (settingsFile.open(QIODevice::WriteOnly)) {
|
if (settingsFile.open(QIODevice::WriteOnly)) {
|
||||||
settingsFile.write(QJsonDocument::fromVariant(_configMap.getUserConfig()).toJson());
|
settingsFile.write(QJsonDocument::fromVariant(_configMap.getUserConfig()).toJson());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,27 +18,28 @@
|
||||||
#include <HifiConfigVariantMap.h>
|
#include <HifiConfigVariantMap.h>
|
||||||
#include <HTTPManager.h>
|
#include <HTTPManager.h>
|
||||||
|
|
||||||
|
const QString SETTINGS_PATHS_KEY = "paths";
|
||||||
|
|
||||||
class DomainServerSettingsManager : public QObject {
|
class DomainServerSettingsManager : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
DomainServerSettingsManager();
|
DomainServerSettingsManager();
|
||||||
bool handlePublicHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
bool handlePublicHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||||
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
bool handleAuthenticatedHTTPRequest(HTTPConnection* connection, const QUrl& url);
|
||||||
|
|
||||||
void setupConfigMap(const QStringList& argumentList);
|
void setupConfigMap(const QStringList& argumentList);
|
||||||
QVariant valueOrDefaultValueForKeyPath(const QString& keyPath);
|
QVariant valueOrDefaultValueForKeyPath(const QString& keyPath);
|
||||||
|
|
||||||
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
|
QVariantMap& getSettingsMap() { return _configMap.getMergedConfig(); }
|
||||||
private:
|
private:
|
||||||
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);
|
QJsonObject responseObjectForType(const QString& typeValue, bool isAuthenticated = false);
|
||||||
void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant,
|
void recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant);
|
||||||
const QJsonArray& descriptionArray);
|
|
||||||
bool settingExists(const QString& groupName, const QString& settingName,
|
|
||||||
const QJsonArray& descriptionArray, QJsonObject& settingDescription);
|
|
||||||
void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
|
void updateSetting(const QString& key, const QJsonValue& newValue, QVariantMap& settingMap,
|
||||||
const QJsonObject& settingDescription);
|
const QJsonObject& settingDescription);
|
||||||
|
QJsonObject settingDescriptionFromGroup(const QJsonObject& groupObject, const QString& settingName);
|
||||||
void persistToFile();
|
void persistToFile();
|
||||||
|
|
||||||
QJsonArray _descriptionArray;
|
QJsonArray _descriptionArray;
|
||||||
HifiConfigVariantMap _configMap;
|
HifiConfigVariantMap _configMap;
|
||||||
};
|
};
|
||||||
|
|
|
@ -260,9 +260,11 @@ function update(deltaTime) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Entities.editEntity(grabbedEntity, {
|
Entities.editEntity(grabbedEntity, {
|
||||||
|
position: currentPosition,
|
||||||
|
rotation: currentRotation,
|
||||||
velocity: newVelocity,
|
velocity: newVelocity,
|
||||||
angularVelocity: angularVelocity
|
angularVelocity: angularVelocity
|
||||||
})
|
});
|
||||||
updateDropLine(targetPosition);
|
updateDropLine(targetPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -633,7 +633,7 @@
|
||||||
elColorBlue.addEventListener('change', colorChangeFunction);
|
elColorBlue.addEventListener('change', colorChangeFunction);
|
||||||
$('#property-color').colpick({
|
$('#property-color').colpick({
|
||||||
colorScheme:'dark',
|
colorScheme:'dark',
|
||||||
layout:'rgbhex',
|
layout:'hex',
|
||||||
color:'000000',
|
color:'000000',
|
||||||
onSubmit: function(hsb, hex, rgb, el) {
|
onSubmit: function(hsb, hex, rgb, el) {
|
||||||
$(el).css('background-color', '#'+hex);
|
$(el).css('background-color', '#'+hex);
|
||||||
|
@ -651,7 +651,7 @@
|
||||||
elLightColorBlue.addEventListener('change', lightColorChangeFunction);
|
elLightColorBlue.addEventListener('change', lightColorChangeFunction);
|
||||||
$('#property-light-color').colpick({
|
$('#property-light-color').colpick({
|
||||||
colorScheme:'dark',
|
colorScheme:'dark',
|
||||||
layout:'rgbhex',
|
layout:'hex',
|
||||||
color:'000000',
|
color:'000000',
|
||||||
onSubmit: function(hsb, hex, rgb, el) {
|
onSubmit: function(hsb, hex, rgb, el) {
|
||||||
$(el).css('background-color', '#'+hex);
|
$(el).css('background-color', '#'+hex);
|
||||||
|
@ -686,7 +686,7 @@
|
||||||
elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction);
|
elTextTextColorBlue.addEventListener('change', textTextColorChangeFunction);
|
||||||
$('#property-text-text-color').colpick({
|
$('#property-text-text-color').colpick({
|
||||||
colorScheme:'dark',
|
colorScheme:'dark',
|
||||||
layout:'rgbhex',
|
layout:'hex',
|
||||||
color:'000000',
|
color:'000000',
|
||||||
onSubmit: function(hsb, hex, rgb, el) {
|
onSubmit: function(hsb, hex, rgb, el) {
|
||||||
$(el).css('background-color', '#'+hex);
|
$(el).css('background-color', '#'+hex);
|
||||||
|
@ -702,7 +702,7 @@
|
||||||
elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
|
elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
|
||||||
$('#property-text-background-color').colpick({
|
$('#property-text-background-color').colpick({
|
||||||
colorScheme:'dark',
|
colorScheme:'dark',
|
||||||
layout:'rgbhex',
|
layout:'hex',
|
||||||
color:'000000',
|
color:'000000',
|
||||||
onSubmit: function(hsb, hex, rgb, el) {
|
onSubmit: function(hsb, hex, rgb, el) {
|
||||||
$(el).css('background-color', '#'+hex);
|
$(el).css('background-color', '#'+hex);
|
||||||
|
@ -714,7 +714,7 @@
|
||||||
elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled'));
|
elZoneStageSunModelEnabled.addEventListener('change', createEmitGroupCheckedPropertyUpdateFunction('stage','sunModelEnabled'));
|
||||||
$('#property-zone-key-light-color').colpick({
|
$('#property-zone-key-light-color').colpick({
|
||||||
colorScheme:'dark',
|
colorScheme:'dark',
|
||||||
layout:'rgbhex',
|
layout:'hex',
|
||||||
color:'000000',
|
color:'000000',
|
||||||
onSubmit: function(hsb, hex, rgb, el) {
|
onSubmit: function(hsb, hex, rgb, el) {
|
||||||
$(el).css('background-color', '#'+hex);
|
$(el).css('background-color', '#'+hex);
|
||||||
|
@ -751,7 +751,7 @@
|
||||||
elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction);
|
elZoneSkyboxColorBlue.addEventListener('change', zoneSkyboxColorChangeFunction);
|
||||||
$('#property-zone-skybox-color').colpick({
|
$('#property-zone-skybox-color').colpick({
|
||||||
colorScheme:'dark',
|
colorScheme:'dark',
|
||||||
layout:'rgbhex',
|
layout:'hex',
|
||||||
color:'000000',
|
color:'000000',
|
||||||
onSubmit: function(hsb, hex, rgb, el) {
|
onSubmit: function(hsb, hex, rgb, el) {
|
||||||
$(el).css('background-color', '#'+hex);
|
$(el).css('background-color', '#'+hex);
|
||||||
|
|
|
@ -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) {
|
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||||
if (event->key() == Qt::Key_Alt && _altPressed && _window->isActiveWindow()) {
|
if (event->key() == Qt::Key_Alt && _altPressed && _window->isActiveWindow()) {
|
||||||
#ifdef VR_MENU_ONLY_IN_HMD
|
#ifdef VR_MENU_ONLY_IN_HMD
|
||||||
if (OculusManager::isConnected()) {
|
if (isHMDMode()) {
|
||||||
#endif
|
#endif
|
||||||
VrMenu::toggle();
|
VrMenu::toggle();
|
||||||
#ifdef VR_MENU_ONLY_IN_HMD
|
#ifdef VR_MENU_ONLY_IN_HMD
|
||||||
|
@ -1949,6 +1949,7 @@ void Application::setActiveFaceTracker() {
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_DDE
|
#ifdef HAVE_DDE
|
||||||
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera);
|
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::UseAudioForMouth)->setVisible(isUsingDDE);
|
||||||
Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE);
|
Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE);
|
||||||
Menu::getInstance()->getActionForOption(MenuOption::CalibrateCamera)->setVisible(isUsingDDE);
|
Menu::getInstance()->getActionForOption(MenuOption::CalibrateCamera)->setVisible(isUsingDDE);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
DatagramProcessor::DatagramProcessor(QObject* parent) :
|
DatagramProcessor::DatagramProcessor(QObject* parent) :
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatagramProcessor::processDatagrams() {
|
void DatagramProcessor::processDatagrams() {
|
||||||
|
@ -35,23 +35,23 @@ void DatagramProcessor::processDatagrams() {
|
||||||
if (_isShuttingDown) {
|
if (_isShuttingDown) {
|
||||||
return; // bail early... we're shutting down.
|
return; // bail early... we're shutting down.
|
||||||
}
|
}
|
||||||
|
|
||||||
HifiSockAddr senderSockAddr;
|
HifiSockAddr senderSockAddr;
|
||||||
|
|
||||||
static QByteArray incomingPacket;
|
static QByteArray incomingPacket;
|
||||||
|
|
||||||
Application* application = Application::getInstance();
|
Application* application = Application::getInstance();
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
while (DependencyManager::get<NodeList>()->getNodeSocket().hasPendingDatagrams()) {
|
while (DependencyManager::get<NodeList>()->getNodeSocket().hasPendingDatagrams()) {
|
||||||
incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
|
incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
|
||||||
nodeList->readDatagram(incomingPacket, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
nodeList->readDatagram(incomingPacket, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
|
||||||
|
|
||||||
_inPacketCount++;
|
_inPacketCount++;
|
||||||
_inByteCount += incomingPacket.size();
|
_inByteCount += incomingPacket.size();
|
||||||
|
|
||||||
if (nodeList->packetVersionAndHashMatch(incomingPacket)) {
|
if (nodeList->packetVersionAndHashMatch(incomingPacket)) {
|
||||||
|
|
||||||
PacketType incomingType = packetTypeForPacket(incomingPacket);
|
PacketType incomingType = packetTypeForPacket(incomingPacket);
|
||||||
// only process this packet if we have a match on the packet version
|
// only process this packet if we have a match on the packet version
|
||||||
switch (incomingType) {
|
switch (incomingType) {
|
||||||
|
@ -72,14 +72,14 @@ void DatagramProcessor::processDatagrams() {
|
||||||
Qt::QueuedConnection,
|
Qt::QueuedConnection,
|
||||||
Q_ARG(QByteArray, incomingPacket));
|
Q_ARG(QByteArray, incomingPacket));
|
||||||
}
|
}
|
||||||
|
|
||||||
// update having heard from the audio-mixer and record the bytes received
|
// update having heard from the audio-mixer and record the bytes received
|
||||||
SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket);
|
SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket);
|
||||||
|
|
||||||
if (audioMixer) {
|
if (audioMixer) {
|
||||||
audioMixer->setLastHeardMicrostamp(usecTimestampNow());
|
audioMixer->setLastHeardMicrostamp(usecTimestampNow());
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PacketTypeEntityAddResponse:
|
case PacketTypeEntityAddResponse:
|
||||||
|
@ -94,7 +94,7 @@ void DatagramProcessor::processDatagrams() {
|
||||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
"Application::networkReceive()... _octreeProcessor.queueReceivedPacket()");
|
"Application::networkReceive()... _octreeProcessor.queueReceivedPacket()");
|
||||||
SharedNodePointer matchedNode = DependencyManager::get<NodeList>()->sendingNodeForPacket(incomingPacket);
|
SharedNodePointer matchedNode = DependencyManager::get<NodeList>()->sendingNodeForPacket(incomingPacket);
|
||||||
|
|
||||||
if (matchedNode) {
|
if (matchedNode) {
|
||||||
// add this packet to our list of octree packets and process them on the octree data processing
|
// add this packet to our list of octree packets and process them on the octree data processing
|
||||||
application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket);
|
application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket);
|
||||||
|
@ -107,10 +107,10 @@ void DatagramProcessor::processDatagrams() {
|
||||||
case PacketTypeAvatarBillboard: {
|
case PacketTypeAvatarBillboard: {
|
||||||
// update having heard from the avatar-mixer and record the bytes received
|
// update having heard from the avatar-mixer and record the bytes received
|
||||||
SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket);
|
SharedNodePointer avatarMixer = nodeList->sendingNodeForPacket(incomingPacket);
|
||||||
|
|
||||||
if (avatarMixer) {
|
if (avatarMixer) {
|
||||||
avatarMixer->setLastHeardMicrostamp(usecTimestampNow());
|
avatarMixer->setLastHeardMicrostamp(usecTimestampNow());
|
||||||
|
|
||||||
QMetaObject::invokeMethod(DependencyManager::get<AvatarManager>().data(), "processAvatarMixerDatagram",
|
QMetaObject::invokeMethod(DependencyManager::get<AvatarManager>().data(), "processAvatarMixerDatagram",
|
||||||
Q_ARG(const QByteArray&, incomingPacket),
|
Q_ARG(const QByteArray&, incomingPacket),
|
||||||
Q_ARG(const QWeakPointer<Node>&, avatarMixer));
|
Q_ARG(const QWeakPointer<Node>&, avatarMixer));
|
||||||
|
@ -135,20 +135,20 @@ void DatagramProcessor::processDatagrams() {
|
||||||
case PacketTypeNoisyMute:
|
case PacketTypeNoisyMute:
|
||||||
case PacketTypeMuteEnvironment: {
|
case PacketTypeMuteEnvironment: {
|
||||||
bool mute = !DependencyManager::get<AudioClient>()->isMuted();
|
bool mute = !DependencyManager::get<AudioClient>()->isMuted();
|
||||||
|
|
||||||
if (incomingType == PacketTypeMuteEnvironment) {
|
if (incomingType == PacketTypeMuteEnvironment) {
|
||||||
glm::vec3 position;
|
glm::vec3 position;
|
||||||
float radius;
|
float radius;
|
||||||
|
|
||||||
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
|
int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment);
|
||||||
memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3));
|
memcpy(&position, incomingPacket.constData() + headerSize, sizeof(glm::vec3));
|
||||||
memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float));
|
memcpy(&radius, incomingPacket.constData() + headerSize + sizeof(glm::vec3), sizeof(float));
|
||||||
float distance = glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(),
|
float distance = glm::distance(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(),
|
||||||
position);
|
position);
|
||||||
|
|
||||||
mute = mute && (distance < radius);
|
mute = mute && (distance < radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mute) {
|
if (mute) {
|
||||||
DependencyManager::get<AudioClient>()->toggleMute();
|
DependencyManager::get<AudioClient>()->toggleMute();
|
||||||
if (incomingType == PacketTypeMuteEnvironment) {
|
if (incomingType == PacketTypeMuteEnvironment) {
|
||||||
|
|
|
@ -388,6 +388,8 @@ Menu::Menu() {
|
||||||
}
|
}
|
||||||
#ifdef HAVE_DDE
|
#ifdef HAVE_DDE
|
||||||
faceTrackingMenu->addSeparator();
|
faceTrackingMenu->addSeparator();
|
||||||
|
QAction* binaryEyelidControl = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::BinaryEyelidControl, 0, true);
|
||||||
|
binaryEyelidControl->setVisible(false);
|
||||||
QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true);
|
QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true);
|
||||||
useAudioForMouth->setVisible(false);
|
useAudioForMouth->setVisible(false);
|
||||||
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);
|
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);
|
||||||
|
|
|
@ -149,6 +149,7 @@ namespace MenuOption {
|
||||||
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
|
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
|
||||||
const QString AvatarReceiveStats = "Show Receive Stats";
|
const QString AvatarReceiveStats = "Show Receive Stats";
|
||||||
const QString BandwidthDetails = "Bandwidth Details";
|
const QString BandwidthDetails = "Bandwidth Details";
|
||||||
|
const QString BinaryEyelidControl = "Binary Eyelid Control";
|
||||||
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
|
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
|
||||||
const QString BookmarkLocation = "Bookmark Location";
|
const QString BookmarkLocation = "Bookmark Location";
|
||||||
const QString Bookmarks = "Bookmarks";
|
const QString Bookmarks = "Bookmarks";
|
||||||
|
|
|
@ -137,7 +137,7 @@ struct Packet {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const float STARTING_DDE_MESSAGE_TIME = 0.033f;
|
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;
|
static const int CALIBRATION_SAMPLES = 150;
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
@ -182,6 +182,7 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui
|
||||||
_lastEyeBlinks(),
|
_lastEyeBlinks(),
|
||||||
_filteredEyeBlinks(),
|
_filteredEyeBlinks(),
|
||||||
_lastEyeCoefficients(),
|
_lastEyeCoefficients(),
|
||||||
|
_eyeClosingThreshold("ddeEyeClosingThreshold", DEFAULT_DDE_EYE_CLOSING_THRESHOLD),
|
||||||
_isCalibrating(false),
|
_isCalibrating(false),
|
||||||
_calibrationCount(0),
|
_calibrationCount(0),
|
||||||
_calibrationValues(),
|
_calibrationValues(),
|
||||||
|
@ -194,8 +195,8 @@ DdeFaceTracker::DdeFaceTracker(const QHostAddress& host, quint16 serverPort, qui
|
||||||
_coefficientAverages.resize(NUM_FACESHIFT_BLENDSHAPES);
|
_coefficientAverages.resize(NUM_FACESHIFT_BLENDSHAPES);
|
||||||
_calibrationValues.resize(NUM_FACESHIFT_BLENDSHAPES);
|
_calibrationValues.resize(NUM_FACESHIFT_BLENDSHAPES);
|
||||||
|
|
||||||
_eyeStates[0] = EYE_OPEN;
|
_eyeStates[0] = EYE_UNCONTROLLED;
|
||||||
_eyeStates[1] = EYE_OPEN;
|
_eyeStates[1] = EYE_UNCONTROLLED;
|
||||||
|
|
||||||
connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
|
connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
|
||||||
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError)));
|
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketErrorOccurred(QAbstractSocket::SocketError)));
|
||||||
|
@ -450,57 +451,72 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
||||||
|
|
||||||
// Finesse EyeBlink values
|
// Finesse EyeBlink values
|
||||||
float eyeCoefficients[2];
|
float eyeCoefficients[2];
|
||||||
for (int i = 0; i < 2; i++) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::BinaryEyelidControl)) {
|
||||||
// Scale EyeBlink values so that they can be used to control both EyeBlink and EyeOpen
|
if (_eyeStates[0] == EYE_UNCONTROLLED) {
|
||||||
// -ve values control EyeOpen; +ve values control EyeBlink
|
_eyeStates[0] = EYE_OPEN;
|
||||||
static const float EYE_CONTROL_THRESHOLD = 0.5f; // Resting eye value
|
_eyeStates[1] = EYE_OPEN;
|
||||||
eyeCoefficients[i] = (_filteredEyeBlinks[i] - EYE_CONTROL_THRESHOLD) / (1.0f - EYE_CONTROL_THRESHOLD);
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
_eyeStates[i] = EYE_CLOSING;
|
|
||||||
} else if ((_eyeStates[i] == EYE_CLOSED || _eyeStates[i] == EYE_CLOSING)
|
|
||||||
&& eyeCoefficients[i] < EYE_OPENING_THRESHOLD) {
|
|
||||||
_eyeStates[i] = EYE_OPENING;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const float EYELID_MOVEMENT_RATE = 10.0f; // units/second
|
for (int i = 0; i < 2; i++) {
|
||||||
const float EYE_OPEN_SCALE = 0.2f;
|
// Scale EyeBlink values so that they can be used to control both EyeBlink and EyeOpen
|
||||||
if (_eyeStates[i] == EYE_CLOSING) {
|
// -ve values control EyeOpen; +ve values control EyeBlink
|
||||||
// Close eyelid until it's fully closed
|
static const float EYE_CONTROL_THRESHOLD = 0.5f; // Resting eye value
|
||||||
float closingValue = _lastEyeCoefficients[i] + EYELID_MOVEMENT_RATE * _averageMessageTime;
|
eyeCoefficients[i] = (_filteredEyeBlinks[i] - EYE_CONTROL_THRESHOLD) / (1.0f - EYE_CONTROL_THRESHOLD);
|
||||||
if (closingValue >= 1.0) {
|
|
||||||
_eyeStates[i] = EYE_CLOSED;
|
// Change to closing or opening states
|
||||||
eyeCoefficients[i] = 1.0;
|
const float EYE_CONTROL_HYSTERISIS = 0.25f;
|
||||||
} else {
|
float eyeClosingThreshold = getEyeClosingThreshold();
|
||||||
eyeCoefficients[i] = closingValue;
|
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] < eyeOpeningThreshold) {
|
||||||
|
_eyeStates[i] = EYE_OPENING;
|
||||||
}
|
}
|
||||||
} else if (_eyeStates[i] == EYE_OPENING) {
|
|
||||||
// Open eyelid until it meets the current adjusted value
|
const float EYELID_MOVEMENT_RATE = 10.0f; // units/second
|
||||||
float openingValue = _lastEyeCoefficients[i] - EYELID_MOVEMENT_RATE * _averageMessageTime;
|
const float EYE_OPEN_SCALE = 0.2f;
|
||||||
if (openingValue < eyeCoefficients[i] * EYE_OPEN_SCALE) {
|
if (_eyeStates[i] == EYE_CLOSING) {
|
||||||
_eyeStates[i] = EYE_OPEN;
|
// Close eyelid until it's fully closed
|
||||||
|
float closingValue = _lastEyeCoefficients[i] + EYELID_MOVEMENT_RATE * _averageMessageTime;
|
||||||
|
if (closingValue >= 1.0) {
|
||||||
|
_eyeStates[i] = EYE_CLOSED;
|
||||||
|
eyeCoefficients[i] = 1.0;
|
||||||
|
} else {
|
||||||
|
eyeCoefficients[i] = closingValue;
|
||||||
|
}
|
||||||
|
} else if (_eyeStates[i] == EYE_OPENING) {
|
||||||
|
// Open eyelid until it meets the current adjusted value
|
||||||
|
float openingValue = _lastEyeCoefficients[i] - EYELID_MOVEMENT_RATE * _averageMessageTime;
|
||||||
|
if (openingValue < eyeCoefficients[i] * EYE_OPEN_SCALE) {
|
||||||
|
_eyeStates[i] = EYE_OPEN;
|
||||||
|
eyeCoefficients[i] = eyeCoefficients[i] * EYE_OPEN_SCALE;
|
||||||
|
} else {
|
||||||
|
eyeCoefficients[i] = openingValue;
|
||||||
|
}
|
||||||
|
} else if (_eyeStates[i] == EYE_OPEN) {
|
||||||
|
// Reduce eyelid movement
|
||||||
eyeCoefficients[i] = eyeCoefficients[i] * EYE_OPEN_SCALE;
|
eyeCoefficients[i] = eyeCoefficients[i] * EYE_OPEN_SCALE;
|
||||||
} else {
|
} else if (_eyeStates[i] == EYE_CLOSED) {
|
||||||
eyeCoefficients[i] = openingValue;
|
// Keep eyelid fully closed
|
||||||
|
eyeCoefficients[i] = 1.0;
|
||||||
}
|
}
|
||||||
} else if (_eyeStates[i] == EYE_OPEN) {
|
|
||||||
// Reduce eyelid movement
|
|
||||||
eyeCoefficients[i] = eyeCoefficients[i] * EYE_OPEN_SCALE;
|
|
||||||
} else if (_eyeStates[i] == EYE_CLOSED) {
|
|
||||||
// Keep eyelid fully closed
|
|
||||||
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];
|
||||||
}
|
}
|
||||||
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];
|
|
||||||
|
|
||||||
// Use EyeBlink values to control both EyeBlink and EyeOpen
|
// Use EyeBlink values to control both EyeBlink and EyeOpen
|
||||||
if (eyeCoefficients[0] > 0) {
|
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_WIDTH = 240;
|
||||||
static const int CALIBRATION_BILLBOARD_HEIGHT = 180;
|
static const int CALIBRATION_BILLBOARD_HEIGHT = 180;
|
||||||
static const int CALIBRATION_BILLBOARD_TOP_MARGIN = 60;
|
static const int CALIBRATION_BILLBOARD_TOP_MARGIN = 60;
|
||||||
|
|
|
@ -50,6 +50,9 @@ public:
|
||||||
float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); }
|
float getMouthSmileLeft() const { return getBlendshapeCoefficient(_mouthSmileLeftIndex); }
|
||||||
float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); }
|
float getMouthSmileRight() const { return getBlendshapeCoefficient(_mouthSmileRightIndex); }
|
||||||
|
|
||||||
|
float getEyeClosingThreshold() { return _eyeClosingThreshold.get(); }
|
||||||
|
void setEyeClosingThreshold(float eyeClosingThreshold);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setEnabled(bool enabled);
|
void setEnabled(bool enabled);
|
||||||
void calibrate();
|
void calibrate();
|
||||||
|
@ -89,8 +92,7 @@ private:
|
||||||
int _rightBlinkIndex;
|
int _rightBlinkIndex;
|
||||||
int _leftEyeOpenIndex;
|
int _leftEyeOpenIndex;
|
||||||
int _rightEyeOpenIndex;
|
int _rightEyeOpenIndex;
|
||||||
|
|
||||||
// Brows
|
|
||||||
int _browDownLeftIndex;
|
int _browDownLeftIndex;
|
||||||
int _browDownRightIndex;
|
int _browDownRightIndex;
|
||||||
int _browUpCenterIndex;
|
int _browUpCenterIndex;
|
||||||
|
@ -114,6 +116,7 @@ private:
|
||||||
float _filteredBrowUp;
|
float _filteredBrowUp;
|
||||||
|
|
||||||
enum EyeState {
|
enum EyeState {
|
||||||
|
EYE_UNCONTROLLED,
|
||||||
EYE_OPEN,
|
EYE_OPEN,
|
||||||
EYE_CLOSING,
|
EYE_CLOSING,
|
||||||
EYE_CLOSED,
|
EYE_CLOSED,
|
||||||
|
@ -123,6 +126,8 @@ private:
|
||||||
float _lastEyeBlinks[2];
|
float _lastEyeBlinks[2];
|
||||||
float _filteredEyeBlinks[2];
|
float _filteredEyeBlinks[2];
|
||||||
float _lastEyeCoefficients[2];
|
float _lastEyeCoefficients[2];
|
||||||
|
Setting::Handle<float> _eyeClosingThreshold;
|
||||||
|
|
||||||
QVector<float> _coefficientAverages;
|
QVector<float> _coefficientAverages;
|
||||||
|
|
||||||
bool _isCalibrating;
|
bool _isCalibrating;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <AudioClient.h>
|
#include <AudioClient.h>
|
||||||
#include <avatar/AvatarManager.h>
|
#include <avatar/AvatarManager.h>
|
||||||
|
#include <devices/DdeFaceTracker.h>
|
||||||
#include <devices/Faceshift.h>
|
#include <devices/Faceshift.h>
|
||||||
#include <devices/SixenseManager.h>
|
#include <devices/SixenseManager.h>
|
||||||
#include <NetworkingConstants.h>
|
#include <NetworkingConstants.h>
|
||||||
|
@ -135,6 +136,10 @@ void PreferencesDialog::loadPreferences() {
|
||||||
ui.pupilDilationSlider->setValue(myAvatar->getHead()->getPupilDilation() *
|
ui.pupilDilationSlider->setValue(myAvatar->getHead()->getPupilDilation() *
|
||||||
ui.pupilDilationSlider->maximum());
|
ui.pupilDilationSlider->maximum());
|
||||||
|
|
||||||
|
auto dde = DependencyManager::get<DdeFaceTracker>();
|
||||||
|
ui.ddeEyeClosingThresholdSlider->setValue(dde->getEyeClosingThreshold() *
|
||||||
|
ui.ddeEyeClosingThresholdSlider->maximum());
|
||||||
|
|
||||||
auto faceshift = DependencyManager::get<Faceshift>();
|
auto faceshift = DependencyManager::get<Faceshift>();
|
||||||
ui.faceshiftEyeDeflectionSider->setValue(faceshift->getEyeDeflection() *
|
ui.faceshiftEyeDeflectionSider->setValue(faceshift->getEyeDeflection() *
|
||||||
ui.faceshiftEyeDeflectionSider->maximum());
|
ui.faceshiftEyeDeflectionSider->maximum());
|
||||||
|
@ -222,6 +227,10 @@ void PreferencesDialog::savePreferences() {
|
||||||
|
|
||||||
qApp->setFieldOfView(ui.fieldOfViewSpin->value());
|
qApp->setFieldOfView(ui.fieldOfViewSpin->value());
|
||||||
|
|
||||||
|
auto dde = DependencyManager::get<DdeFaceTracker>();
|
||||||
|
dde->setEyeClosingThreshold(ui.ddeEyeClosingThresholdSlider->value() /
|
||||||
|
(float)ui.ddeEyeClosingThresholdSlider->maximum());
|
||||||
|
|
||||||
auto faceshift = DependencyManager::get<Faceshift>();
|
auto faceshift = DependencyManager::get<Faceshift>();
|
||||||
faceshift->setEyeDeflection(ui.faceshiftEyeDeflectionSider->value() /
|
faceshift->setEyeDeflection(ui.faceshiftEyeDeflectionSider->value() /
|
||||||
(float)ui.faceshiftEyeDeflectionSider->maximum());
|
(float)ui.faceshiftEyeDeflectionSider->maximum());
|
||||||
|
|
|
@ -1256,7 +1256,7 @@
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Pupil dillation</string>
|
<string>Pupil dilation</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="indent">
|
<property name="indent">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
|
@ -1310,6 +1310,82 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
|
|
|
@ -43,11 +43,11 @@ bool AddressManager::isConnected() {
|
||||||
|
|
||||||
const QUrl AddressManager::currentAddress() const {
|
const QUrl AddressManager::currentAddress() const {
|
||||||
QUrl hifiURL;
|
QUrl hifiURL;
|
||||||
|
|
||||||
hifiURL.setScheme(HIFI_URL_SCHEME);
|
hifiURL.setScheme(HIFI_URL_SCHEME);
|
||||||
hifiURL.setHost(_rootPlaceName);
|
hifiURL.setHost(_rootPlaceName);
|
||||||
hifiURL.setPath(currentPath());
|
hifiURL.setPath(currentPath());
|
||||||
|
|
||||||
return hifiURL;
|
return hifiURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,10 +64,10 @@ void AddressManager::storeCurrentAddress() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString AddressManager::currentPath(bool withOrientation) const {
|
const QString AddressManager::currentPath(bool withOrientation) const {
|
||||||
|
|
||||||
if (_positionGetter) {
|
if (_positionGetter) {
|
||||||
QString pathString = "/" + createByteArray(_positionGetter());
|
QString pathString = "/" + createByteArray(_positionGetter());
|
||||||
|
|
||||||
if (withOrientation) {
|
if (withOrientation) {
|
||||||
if (_orientationGetter) {
|
if (_orientationGetter) {
|
||||||
QString orientationString = createByteArray(_orientationGetter());
|
QString orientationString = createByteArray(_orientationGetter());
|
||||||
|
@ -76,9 +76,9 @@ const QString AddressManager::currentPath(bool withOrientation) const {
|
||||||
qCDebug(networking) << "Cannot add orientation to path without a getter for position."
|
qCDebug(networking) << "Cannot add orientation to path without a getter for position."
|
||||||
<< "Call AddressManager::setOrientationGetter to pass a function that will return a glm::quat";
|
<< "Call AddressManager::setOrientationGetter to pass a function that will return a glm::quat";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathString;
|
return pathString;
|
||||||
} else {
|
} else {
|
||||||
qCDebug(networking) << "Cannot create address path without a getter for position."
|
qCDebug(networking) << "Cannot create address path without a getter for position."
|
||||||
|
@ -90,29 +90,29 @@ const QString AddressManager::currentPath(bool withOrientation) const {
|
||||||
const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
|
const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
|
||||||
static bool hasSetupParameters = false;
|
static bool hasSetupParameters = false;
|
||||||
static JSONCallbackParameters callbackParams;
|
static JSONCallbackParameters callbackParams;
|
||||||
|
|
||||||
if (!hasSetupParameters) {
|
if (!hasSetupParameters) {
|
||||||
callbackParams.jsonCallbackReceiver = this;
|
callbackParams.jsonCallbackReceiver = this;
|
||||||
callbackParams.jsonCallbackMethod = "handleAPIResponse";
|
callbackParams.jsonCallbackMethod = "handleAPIResponse";
|
||||||
callbackParams.errorCallbackReceiver = this;
|
callbackParams.errorCallbackReceiver = this;
|
||||||
callbackParams.errorCallbackMethod = "handleAPIError";
|
callbackParams.errorCallbackMethod = "handleAPIError";
|
||||||
}
|
}
|
||||||
|
|
||||||
return callbackParams;
|
return callbackParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddressManager::handleUrl(const QUrl& lookupUrl) {
|
bool AddressManager::handleUrl(const QUrl& lookupUrl) {
|
||||||
if (lookupUrl.scheme() == HIFI_URL_SCHEME) {
|
if (lookupUrl.scheme() == HIFI_URL_SCHEME) {
|
||||||
|
|
||||||
qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString();
|
qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString();
|
||||||
|
|
||||||
// there are 4 possible lookup strings
|
// there are 4 possible lookup strings
|
||||||
|
|
||||||
// 1. global place name (name of domain or place) - example: sanfrancisco
|
// 1. global place name (name of domain or place) - example: sanfrancisco
|
||||||
// 2. user name (prepended with @) - example: @philip
|
// 2. user name (prepended with @) - example: @philip
|
||||||
// 3. location string (posX,posY,posZ/eulerX,eulerY,eulerZ)
|
// 3. location string (posX,posY,posZ/eulerX,eulerY,eulerZ)
|
||||||
// 4. domain network address (IP or dns resolvable hostname)
|
// 4. domain network address (IP or dns resolvable hostname)
|
||||||
|
|
||||||
// use our regex'ed helpers to figure out what we're supposed to do with this
|
// use our regex'ed helpers to figure out what we're supposed to do with this
|
||||||
if (!handleUsername(lookupUrl.authority())) {
|
if (!handleUsername(lookupUrl.authority())) {
|
||||||
// we're assuming this is either a network address or global place name
|
// we're assuming this is either a network address or global place name
|
||||||
|
@ -120,24 +120,23 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
|
||||||
if (handleNetworkAddress(lookupUrl.host()
|
if (handleNetworkAddress(lookupUrl.host()
|
||||||
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) {
|
+ (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
|
// 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 {
|
} else {
|
||||||
// wasn't an address - lookup the place name
|
// 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
|
// 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());
|
attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if (lookupUrl.toString().startsWith('/')) {
|
} else if (lookupUrl.toString().startsWith('/')) {
|
||||||
qCDebug(networking) << "Going to relative path" << lookupUrl.path();
|
qCDebug(networking) << "Going to relative path" << lookupUrl.path();
|
||||||
|
|
||||||
// if this is a relative path then handle it as a relative viewpoint
|
// if this is a relative path then handle it as a relative viewpoint
|
||||||
handleRelativeViewpoint(lookupUrl.path());
|
handlePath(lookupUrl.path());
|
||||||
emit lookupResultsFinished();
|
emit lookupResultsFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,16 +145,16 @@ void AddressManager::handleLookupString(const QString& lookupString) {
|
||||||
// make this a valid hifi URL and handle it off to handleUrl
|
// make this a valid hifi URL and handle it off to handleUrl
|
||||||
QString sanitizedString = lookupString.trimmed();
|
QString sanitizedString = lookupString.trimmed();
|
||||||
QUrl lookupURL;
|
QUrl lookupURL;
|
||||||
|
|
||||||
if (!lookupString.startsWith('/')) {
|
if (!lookupString.startsWith('/')) {
|
||||||
const QRegExp HIFI_SCHEME_REGEX = QRegExp(HIFI_URL_SCHEME + ":\\/{1,2}", Qt::CaseInsensitive);
|
const QRegExp HIFI_SCHEME_REGEX = QRegExp(HIFI_URL_SCHEME + ":\\/{1,2}", Qt::CaseInsensitive);
|
||||||
sanitizedString = sanitizedString.remove(HIFI_SCHEME_REGEX);
|
sanitizedString = sanitizedString.remove(HIFI_SCHEME_REGEX);
|
||||||
|
|
||||||
lookupURL = QUrl(HIFI_URL_SCHEME + "://" + sanitizedString);
|
lookupURL = QUrl(HIFI_URL_SCHEME + "://" + sanitizedString);
|
||||||
} else {
|
} else {
|
||||||
lookupURL = QUrl(lookupString);
|
lookupURL = QUrl(lookupString);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUrl(lookupURL);
|
handleUrl(lookupURL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,101 +162,103 @@ void AddressManager::handleLookupString(const QString& lookupString) {
|
||||||
void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
|
void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
|
||||||
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
||||||
QJsonObject dataObject = responseObject["data"].toObject();
|
QJsonObject dataObject = responseObject["data"].toObject();
|
||||||
|
|
||||||
goToAddressFromObject(dataObject.toVariantMap(), requestReply);
|
goToAddressFromObject(dataObject.toVariantMap(), requestReply);
|
||||||
|
|
||||||
emit lookupResultsFinished();
|
emit lookupResultsFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char OVERRIDE_PATH_KEY[] = "override_path";
|
const char OVERRIDE_PATH_KEY[] = "override_path";
|
||||||
|
|
||||||
void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply& reply) {
|
void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply& reply) {
|
||||||
|
|
||||||
const QString DATA_OBJECT_PLACE_KEY = "place";
|
const QString DATA_OBJECT_PLACE_KEY = "place";
|
||||||
const QString DATA_OBJECT_USER_LOCATION_KEY = "location";
|
const QString DATA_OBJECT_USER_LOCATION_KEY = "location";
|
||||||
|
|
||||||
QVariantMap locationMap;
|
QVariantMap locationMap;
|
||||||
if (dataObject.contains(DATA_OBJECT_PLACE_KEY)) {
|
if (dataObject.contains(DATA_OBJECT_PLACE_KEY)) {
|
||||||
locationMap = dataObject[DATA_OBJECT_PLACE_KEY].toMap();
|
locationMap = dataObject[DATA_OBJECT_PLACE_KEY].toMap();
|
||||||
} else {
|
} else {
|
||||||
locationMap = dataObject[DATA_OBJECT_USER_LOCATION_KEY].toMap();
|
locationMap = dataObject[DATA_OBJECT_USER_LOCATION_KEY].toMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!locationMap.isEmpty()) {
|
if (!locationMap.isEmpty()) {
|
||||||
const QString LOCATION_API_ROOT_KEY = "root";
|
const QString LOCATION_API_ROOT_KEY = "root";
|
||||||
const QString LOCATION_API_DOMAIN_KEY = "domain";
|
const QString LOCATION_API_DOMAIN_KEY = "domain";
|
||||||
const QString LOCATION_API_ONLINE_KEY = "online";
|
const QString LOCATION_API_ONLINE_KEY = "online";
|
||||||
|
|
||||||
if (!locationMap.contains(LOCATION_API_ONLINE_KEY)
|
if (!locationMap.contains(LOCATION_API_ONLINE_KEY)
|
||||||
|| locationMap[LOCATION_API_ONLINE_KEY].toBool()) {
|
|| locationMap[LOCATION_API_ONLINE_KEY].toBool()) {
|
||||||
|
|
||||||
QVariantMap rootMap = locationMap[LOCATION_API_ROOT_KEY].toMap();
|
QVariantMap rootMap = locationMap[LOCATION_API_ROOT_KEY].toMap();
|
||||||
if (rootMap.isEmpty()) {
|
if (rootMap.isEmpty()) {
|
||||||
rootMap = locationMap;
|
rootMap = locationMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap domainObject = rootMap[LOCATION_API_DOMAIN_KEY].toMap();
|
QVariantMap domainObject = rootMap[LOCATION_API_DOMAIN_KEY].toMap();
|
||||||
|
|
||||||
if (!domainObject.isEmpty()) {
|
if (!domainObject.isEmpty()) {
|
||||||
const QString DOMAIN_NETWORK_ADDRESS_KEY = "network_address";
|
const QString DOMAIN_NETWORK_ADDRESS_KEY = "network_address";
|
||||||
const QString DOMAIN_NETWORK_PORT_KEY = "network_port";
|
const QString DOMAIN_NETWORK_PORT_KEY = "network_port";
|
||||||
const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address";
|
const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address";
|
||||||
|
|
||||||
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
|
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
|
||||||
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
|
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
|
||||||
|
|
||||||
quint16 domainPort = domainObject.contains(DOMAIN_NETWORK_PORT_KEY)
|
quint16 domainPort = domainObject.contains(DOMAIN_NETWORK_PORT_KEY)
|
||||||
? domainObject[DOMAIN_NETWORK_PORT_KEY].toUInt()
|
? domainObject[DOMAIN_NETWORK_PORT_KEY].toUInt()
|
||||||
: DEFAULT_DOMAIN_SERVER_PORT;
|
: DEFAULT_DOMAIN_SERVER_PORT;
|
||||||
|
|
||||||
qCDebug(networking) << "Possible domain change required to connect to" << domainHostname
|
qCDebug(networking) << "Possible domain change required to connect to" << domainHostname
|
||||||
<< "on" << domainPort;
|
<< "on" << domainPort;
|
||||||
emit possibleDomainChangeRequired(domainHostname, domainPort);
|
emit possibleDomainChangeRequired(domainHostname, domainPort);
|
||||||
} else {
|
} else {
|
||||||
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
|
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
|
||||||
|
|
||||||
const QString DOMAIN_ID_KEY = "id";
|
const QString DOMAIN_ID_KEY = "id";
|
||||||
QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
|
QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
|
||||||
QUuid domainID(domainIDString);
|
QUuid domainID(domainIDString);
|
||||||
|
|
||||||
qCDebug(networking) << "Possible domain change required to connect to domain with ID" << domainID
|
qCDebug(networking) << "Possible domain change required to connect to domain with ID" << domainID
|
||||||
<< "via ice-server at" << iceServerAddress;
|
<< "via ice-server at" << iceServerAddress;
|
||||||
|
|
||||||
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
|
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set our current root place id to the ID that came back
|
// set our current root place id to the ID that came back
|
||||||
const QString PLACE_ID_KEY = "id";
|
const QString PLACE_ID_KEY = "id";
|
||||||
_rootPlaceID = rootMap[PLACE_ID_KEY].toUuid();
|
_rootPlaceID = rootMap[PLACE_ID_KEY].toUuid();
|
||||||
|
|
||||||
// set our current root place name to the name that came back
|
// set our current root place name to the name that came back
|
||||||
const QString PLACE_NAME_KEY = "name";
|
const QString PLACE_NAME_KEY = "name";
|
||||||
QString newRootPlaceName = rootMap[PLACE_NAME_KEY].toString();
|
QString newRootPlaceName = rootMap[PLACE_NAME_KEY].toString();
|
||||||
setRootPlaceName(newRootPlaceName);
|
setRootPlaceName(newRootPlaceName);
|
||||||
|
|
||||||
// check if we had a path to override the path returned
|
// check if we had a path to override the path returned
|
||||||
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
|
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
|
||||||
|
|
||||||
if (!overridePath.isEmpty()) {
|
if (!overridePath.isEmpty()) {
|
||||||
if (!handleRelativeViewpoint(overridePath)){
|
handlePath(overridePath);
|
||||||
qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << overridePath;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// take the path that came back
|
// take the path that came back
|
||||||
const QString PLACE_PATH_KEY = "path";
|
const QString PLACE_PATH_KEY = "path";
|
||||||
QString returnedPath = locationMap[PLACE_PATH_KEY].toString();
|
QString returnedPath = locationMap[PLACE_PATH_KEY].toString();
|
||||||
|
|
||||||
bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY);
|
bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY);
|
||||||
|
|
||||||
if (!returnedPath.isEmpty()) {
|
if (!returnedPath.isEmpty()) {
|
||||||
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
|
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
|
||||||
if (!handleRelativeViewpoint(returnedPath, shouldFaceViewpoint)) {
|
if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) {
|
||||||
qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -" << returnedPath;
|
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 {
|
} else {
|
||||||
qCDebug(networking) << "Received an address manager API response with no domain key. Cannot parse.";
|
qCDebug(networking) << "Received an address manager API response with no domain key. Cannot parse.";
|
||||||
qCDebug(networking) << locationMap;
|
qCDebug(networking) << locationMap;
|
||||||
|
@ -274,7 +275,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
|
||||||
|
|
||||||
void AddressManager::handleAPIError(QNetworkReply& errorReply) {
|
void AddressManager::handleAPIError(QNetworkReply& errorReply) {
|
||||||
qCDebug(networking) << "AddressManager API error -" << errorReply.error() << "-" << errorReply.errorString();
|
qCDebug(networking) << "AddressManager API error -" << errorReply.error() << "-" << errorReply.errorString();
|
||||||
|
|
||||||
if (errorReply.error() == QNetworkReply::ContentNotFoundError) {
|
if (errorReply.error() == QNetworkReply::ContentNotFoundError) {
|
||||||
emit lookupResultIsNotFound();
|
emit lookupResultIsNotFound();
|
||||||
}
|
}
|
||||||
|
@ -286,12 +287,12 @@ const QString GET_PLACE = "/api/v1/places/%1";
|
||||||
void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath) {
|
void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath) {
|
||||||
// assume this is a place name and see if we can get any info on it
|
// assume this is a place name and see if we can get any info on it
|
||||||
QString placeName = QUrl::toPercentEncoding(lookupString);
|
QString placeName = QUrl::toPercentEncoding(lookupString);
|
||||||
|
|
||||||
QVariantMap requestParams;
|
QVariantMap requestParams;
|
||||||
if (!overridePath.isEmpty()) {
|
if (!overridePath.isEmpty()) {
|
||||||
requestParams.insert(OVERRIDE_PATH_KEY, overridePath);
|
requestParams.insert(OVERRIDE_PATH_KEY, overridePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountManager::getInstance().sendRequest(GET_PLACE.arg(placeName),
|
AccountManager::getInstance().sendRequest(GET_PLACE.arg(placeName),
|
||||||
AccountManagerAuth::None,
|
AccountManagerAuth::None,
|
||||||
QNetworkAccessManager::GetOperation,
|
QNetworkAccessManager::GetOperation,
|
||||||
|
@ -302,47 +303,55 @@ void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const Q
|
||||||
bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
||||||
const QString IP_ADDRESS_REGEX_STRING = "^((?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}"
|
const QString IP_ADDRESS_REGEX_STRING = "^((?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}"
|
||||||
"(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(?::(\\d{1,5}))?$";
|
"(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(?::(\\d{1,5}))?$";
|
||||||
|
|
||||||
const QString HOSTNAME_REGEX_STRING = "^((?:[A-Z0-9]|[A-Z0-9][A-Z0-9\\-]{0,61}[A-Z0-9])"
|
const QString HOSTNAME_REGEX_STRING = "^((?:[A-Z0-9]|[A-Z0-9][A-Z0-9\\-]{0,61}[A-Z0-9])"
|
||||||
"(?:\\.(?:[A-Z0-9]|[A-Z0-9][A-Z0-9\\-]{0,61}[A-Z0-9]))+|localhost)(?::(\\d{1,5}))?$";
|
"(?:\\.(?:[A-Z0-9]|[A-Z0-9][A-Z0-9\\-]{0,61}[A-Z0-9]))+|localhost)(?::(\\d{1,5}))?$";
|
||||||
|
|
||||||
QRegExp ipAddressRegex(IP_ADDRESS_REGEX_STRING);
|
QRegExp ipAddressRegex(IP_ADDRESS_REGEX_STRING);
|
||||||
|
|
||||||
if (ipAddressRegex.indexIn(lookupString) != -1) {
|
if (ipAddressRegex.indexIn(lookupString) != -1) {
|
||||||
QString domainIPString = ipAddressRegex.cap(1);
|
QString domainIPString = ipAddressRegex.cap(1);
|
||||||
|
|
||||||
qint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
qint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||||
if (!ipAddressRegex.cap(2).isEmpty()) {
|
if (!ipAddressRegex.cap(2).isEmpty()) {
|
||||||
domainPort = (qint16) ipAddressRegex.cap(2).toInt();
|
domainPort = (qint16) ipAddressRegex.cap(2).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit lookupResultsFinished();
|
emit lookupResultsFinished();
|
||||||
setDomainInfo(domainIPString, domainPort);
|
setDomainInfo(domainIPString, domainPort);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QRegExp hostnameRegex(HOSTNAME_REGEX_STRING, Qt::CaseInsensitive);
|
QRegExp hostnameRegex(HOSTNAME_REGEX_STRING, Qt::CaseInsensitive);
|
||||||
|
|
||||||
if (hostnameRegex.indexIn(lookupString) != -1) {
|
if (hostnameRegex.indexIn(lookupString) != -1) {
|
||||||
QString domainHostname = hostnameRegex.cap(1);
|
QString domainHostname = hostnameRegex.cap(1);
|
||||||
|
|
||||||
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||||
|
|
||||||
if (!hostnameRegex.cap(2).isEmpty()) {
|
if (!hostnameRegex.cap(2).isEmpty()) {
|
||||||
domainPort = (qint16) hostnameRegex.cap(2).toInt();
|
domainPort = (qint16) hostnameRegex.cap(2).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit lookupResultsFinished();
|
emit lookupResultsFinished();
|
||||||
setDomainInfo(domainHostname, domainPort);
|
setDomainInfo(domainHostname, domainPort);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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 FLOAT_REGEX_STRING = "([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
|
||||||
const QString SPACED_COMMA_REGEX_STRING = "\\s*,\\s*";
|
const QString SPACED_COMMA_REGEX_STRING = "\\s*,\\s*";
|
||||||
const QString POSITION_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
|
const QString POSITION_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
|
||||||
|
@ -350,29 +359,29 @@ bool AddressManager::handleRelativeViewpoint(const QString& lookupString, bool s
|
||||||
const QString QUAT_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
|
const QString QUAT_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
|
||||||
FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
|
FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
|
||||||
FLOAT_REGEX_STRING + "\\s*$";
|
FLOAT_REGEX_STRING + "\\s*$";
|
||||||
|
|
||||||
QRegExp positionRegex(POSITION_REGEX_STRING);
|
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
|
// we have at least a position, so emit our signal to say we need to change position
|
||||||
glm::vec3 newPosition(positionRegex.cap(1).toFloat(),
|
glm::vec3 newPosition(positionRegex.cap(1).toFloat(),
|
||||||
positionRegex.cap(2).toFloat(),
|
positionRegex.cap(2).toFloat(),
|
||||||
positionRegex.cap(3).toFloat());
|
positionRegex.cap(3).toFloat());
|
||||||
|
|
||||||
if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) {
|
if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) {
|
||||||
glm::quat newOrientation;
|
glm::quat newOrientation;
|
||||||
|
|
||||||
QRegExp orientationRegex(QUAT_REGEX_STRING);
|
QRegExp orientationRegex(QUAT_REGEX_STRING);
|
||||||
|
|
||||||
// we may also have an orientation
|
// we may also have an orientation
|
||||||
if (lookupString[positionRegex.matchedLength() - 1] == QChar('/')
|
if (viewpointString[positionRegex.matchedLength() - 1] == QChar('/')
|
||||||
&& orientationRegex.indexIn(lookupString, positionRegex.matchedLength() - 1) != -1) {
|
&& orientationRegex.indexIn(viewpointString, positionRegex.matchedLength() - 1) != -1) {
|
||||||
|
|
||||||
glm::quat newOrientation = glm::normalize(glm::quat(orientationRegex.cap(4).toFloat(),
|
glm::quat newOrientation = glm::normalize(glm::quat(orientationRegex.cap(4).toFloat(),
|
||||||
orientationRegex.cap(1).toFloat(),
|
orientationRegex.cap(1).toFloat(),
|
||||||
orientationRegex.cap(2).toFloat(),
|
orientationRegex.cap(2).toFloat(),
|
||||||
orientationRegex.cap(3).toFloat()));
|
orientationRegex.cap(3).toFloat()));
|
||||||
|
|
||||||
if (!isNaN(newOrientation.x) && !isNaN(newOrientation.y) && !isNaN(newOrientation.z)
|
if (!isNaN(newOrientation.x) && !isNaN(newOrientation.y) && !isNaN(newOrientation.z)
|
||||||
&& !isNaN(newOrientation.w)) {
|
&& !isNaN(newOrientation.w)) {
|
||||||
emit locationChangeRequired(newPosition, true, newOrientation, shouldFace);
|
emit locationChangeRequired(newPosition, true, newOrientation, shouldFace);
|
||||||
|
@ -381,31 +390,31 @@ bool AddressManager::handleRelativeViewpoint(const QString& lookupString, bool s
|
||||||
qCDebug(networking) << "Orientation parsed from lookup string is invalid. Will not use for location change.";
|
qCDebug(networking) << "Orientation parsed from lookup string is invalid. Will not use for location change.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit locationChangeRequired(newPosition, false, newOrientation, shouldFace);
|
emit locationChangeRequired(newPosition, false, newOrientation, shouldFace);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qCDebug(networking) << "Could not jump to position from lookup string because it has an invalid value.";
|
qCDebug(networking) << "Could not jump to position from lookup string because it has an invalid value.";
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString GET_USER_LOCATION = "/api/v1/users/%1/location";
|
const QString GET_USER_LOCATION = "/api/v1/users/%1/location";
|
||||||
|
|
||||||
bool AddressManager::handleUsername(const QString& lookupString) {
|
bool AddressManager::handleUsername(const QString& lookupString) {
|
||||||
const QString USERNAME_REGEX_STRING = "^@(\\S+)";
|
const QString USERNAME_REGEX_STRING = "^@(\\S+)";
|
||||||
|
|
||||||
QRegExp usernameRegex(USERNAME_REGEX_STRING);
|
QRegExp usernameRegex(USERNAME_REGEX_STRING);
|
||||||
|
|
||||||
if (usernameRegex.indexIn(lookupString) != -1) {
|
if (usernameRegex.indexIn(lookupString) != -1) {
|
||||||
goToUser(usernameRegex.cap(1));
|
goToUser(usernameRegex.cap(1));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,9 +429,9 @@ void AddressManager::setRootPlaceName(const QString& rootPlaceName) {
|
||||||
void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
|
void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
|
||||||
_rootPlaceName = hostname;
|
_rootPlaceName = hostname;
|
||||||
_rootPlaceID = QUuid();
|
_rootPlaceID = QUuid();
|
||||||
|
|
||||||
qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port;
|
qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port;
|
||||||
|
|
||||||
emit possibleDomainChangeRequired(hostname, port);
|
emit possibleDomainChangeRequired(hostname, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
const QString HIFI_URL_SCHEME = "hifi";
|
const QString HIFI_URL_SCHEME = "hifi";
|
||||||
const QString DEFAULT_HIFI_ADDRESS = "hifi://entry";
|
const QString DEFAULT_HIFI_ADDRESS = "hifi://entry";
|
||||||
|
const QString INDEX_PATH = "/";
|
||||||
|
|
||||||
typedef const glm::vec3& (*PositionGetter)();
|
typedef const glm::vec3& (*PositionGetter)();
|
||||||
typedef glm::quat (*OrientationGetter)();
|
typedef glm::quat (*OrientationGetter)();
|
||||||
|
@ -39,32 +40,34 @@ class AddressManager : public QObject, public Dependency {
|
||||||
public:
|
public:
|
||||||
bool isConnected();
|
bool isConnected();
|
||||||
const QString& getProtocol() { return HIFI_URL_SCHEME; };
|
const QString& getProtocol() { return HIFI_URL_SCHEME; };
|
||||||
|
|
||||||
const QUrl currentAddress() const;
|
const QUrl currentAddress() const;
|
||||||
const QString currentPath(bool withOrientation = true) const;
|
const QString currentPath(bool withOrientation = true) const;
|
||||||
|
|
||||||
const QUuid& getRootPlaceID() const { return _rootPlaceID; }
|
const QUuid& getRootPlaceID() const { return _rootPlaceID; }
|
||||||
|
|
||||||
const QString& getRootPlaceName() const { return _rootPlaceName; }
|
const QString& getRootPlaceName() const { return _rootPlaceName; }
|
||||||
void setRootPlaceName(const QString& rootPlaceName);
|
void setRootPlaceName(const QString& rootPlaceName);
|
||||||
|
|
||||||
void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString());
|
void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString());
|
||||||
|
|
||||||
void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; }
|
void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; }
|
||||||
void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
|
void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
|
||||||
|
|
||||||
void loadSettings(const QString& lookupString = QString());
|
void loadSettings(const QString& lookupString = QString());
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void handleLookupString(const QString& lookupString);
|
void handleLookupString(const QString& lookupString);
|
||||||
|
|
||||||
void goToUser(const QString& username);
|
void goToUser(const QString& username);
|
||||||
void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply);
|
void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply);
|
||||||
|
bool goToViewpoint(const QString& viewpointString) { return handleViewpoint(viewpointString); }
|
||||||
|
|
||||||
void storeCurrentAddress();
|
void storeCurrentAddress();
|
||||||
|
|
||||||
void copyAddress();
|
void copyAddress();
|
||||||
void copyPath();
|
void copyPath();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void lookupResultsFinished();
|
void lookupResultsFinished();
|
||||||
void lookupResultIsOffline();
|
void lookupResultIsOffline();
|
||||||
|
@ -74,6 +77,7 @@ signals:
|
||||||
void locationChangeRequired(const glm::vec3& newPosition,
|
void locationChangeRequired(const glm::vec3& newPosition,
|
||||||
bool hasOrientationChange, const glm::quat& newOrientation,
|
bool hasOrientationChange, const glm::quat& newOrientation,
|
||||||
bool shouldFaceLocation);
|
bool shouldFaceLocation);
|
||||||
|
void pathChangeRequired(const QString& newPath);
|
||||||
void rootPlaceNameChanged(const QString& newRootPlaceName);
|
void rootPlaceNameChanged(const QString& newRootPlaceName);
|
||||||
protected:
|
protected:
|
||||||
AddressManager();
|
AddressManager();
|
||||||
|
@ -82,15 +86,16 @@ private slots:
|
||||||
void handleAPIError(QNetworkReply& errorReply);
|
void handleAPIError(QNetworkReply& errorReply);
|
||||||
private:
|
private:
|
||||||
void setDomainInfo(const QString& hostname, quint16 port);
|
void setDomainInfo(const QString& hostname, quint16 port);
|
||||||
|
|
||||||
const JSONCallbackParameters& apiCallbackParameters();
|
const JSONCallbackParameters& apiCallbackParameters();
|
||||||
|
|
||||||
bool handleUrl(const QUrl& lookupUrl);
|
bool handleUrl(const QUrl& lookupUrl);
|
||||||
|
|
||||||
bool handleNetworkAddress(const QString& lookupString);
|
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);
|
bool handleUsername(const QString& lookupString);
|
||||||
|
|
||||||
QString _rootPlaceName;
|
QString _rootPlaceName;
|
||||||
QUuid _rootPlaceID;
|
QUuid _rootPlaceID;
|
||||||
PositionGetter _positionGetter;
|
PositionGetter _positionGetter;
|
||||||
|
|
|
@ -36,19 +36,19 @@ DomainHandler::DomainHandler(QObject* parent) :
|
||||||
_settingsObject(),
|
_settingsObject(),
|
||||||
_failedSettingsRequests(0)
|
_failedSettingsRequests(0)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainHandler::clearConnectionInfo() {
|
void DomainHandler::clearConnectionInfo() {
|
||||||
_uuid = QUuid();
|
_uuid = QUuid();
|
||||||
|
|
||||||
_icePeer = NetworkPeer();
|
_icePeer = NetworkPeer();
|
||||||
|
|
||||||
if (requiresICE()) {
|
if (requiresICE()) {
|
||||||
// if we connected to this domain with ICE, re-set the socket so we reconnect through the ice-server
|
// 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);
|
setIsConnected(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,12 +65,15 @@ void DomainHandler::softReset() {
|
||||||
|
|
||||||
void DomainHandler::hardReset() {
|
void DomainHandler::hardReset() {
|
||||||
softReset();
|
softReset();
|
||||||
|
|
||||||
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
|
qCDebug(networking) << "Hard reset in NodeList DomainHandler.";
|
||||||
_iceDomainID = QUuid();
|
_iceDomainID = QUuid();
|
||||||
_iceServerSockAddr = HifiSockAddr();
|
_iceServerSockAddr = HifiSockAddr();
|
||||||
_hostname = QString();
|
_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) {
|
void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname) {
|
||||||
|
@ -80,7 +83,7 @@ void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hos
|
||||||
// change the sockAddr
|
// change the sockAddr
|
||||||
_sockAddr = sockAddr;
|
_sockAddr = sockAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// some callers may pass a hostname, this is not to be used for lookup but for DTLS certificate verification
|
// some callers may pass a hostname, this is not to be used for lookup but for DTLS certificate verification
|
||||||
_hostname = hostname;
|
_hostname = hostname;
|
||||||
}
|
}
|
||||||
|
@ -93,29 +96,29 @@ void DomainHandler::setUUID(const QUuid& uuid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) {
|
void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) {
|
||||||
|
|
||||||
if (hostname != _hostname || _sockAddr.getPort() != port) {
|
if (hostname != _hostname || _sockAddr.getPort() != port) {
|
||||||
// re-set the domain info so that auth information is reloaded
|
// re-set the domain info so that auth information is reloaded
|
||||||
hardReset();
|
hardReset();
|
||||||
|
|
||||||
if (hostname != _hostname) {
|
if (hostname != _hostname) {
|
||||||
// set the new hostname
|
// set the new hostname
|
||||||
_hostname = hostname;
|
_hostname = hostname;
|
||||||
|
|
||||||
qCDebug(networking) << "Updated domain hostname to" << _hostname;
|
qCDebug(networking) << "Updated domain hostname to" << _hostname;
|
||||||
|
|
||||||
// re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname
|
// re-set the sock addr to null and fire off a lookup of the IP address for this domain-server's hostname
|
||||||
qCDebug(networking, "Looking up DS hostname %s.", _hostname.toLocal8Bit().constData());
|
qCDebug(networking, "Looking up DS hostname %s.", _hostname.toLocal8Bit().constData());
|
||||||
QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&)));
|
QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&)));
|
||||||
|
|
||||||
UserActivityLogger::getInstance().changedDomain(_hostname);
|
UserActivityLogger::getInstance().changedDomain(_hostname);
|
||||||
emit hostnameChanged(_hostname);
|
emit hostnameChanged(_hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_sockAddr.getPort() != port) {
|
if (_sockAddr.getPort() != port) {
|
||||||
qCDebug(networking) << "Updated domain port to" << port;
|
qCDebug(networking) << "Updated domain port to" << port;
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab the port by reading the string after the colon
|
// grab the port by reading the string after the colon
|
||||||
_sockAddr.setPort(port);
|
_sockAddr.setPort(port);
|
||||||
}
|
}
|
||||||
|
@ -125,16 +128,16 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
|
||||||
if (id != _uuid) {
|
if (id != _uuid) {
|
||||||
// re-set the domain info to connect to new domain
|
// re-set the domain info to connect to new domain
|
||||||
hardReset();
|
hardReset();
|
||||||
|
|
||||||
_iceDomainID = id;
|
_iceDomainID = id;
|
||||||
|
|
||||||
HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr;
|
HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr;
|
||||||
replaceableSockAddr->~HifiSockAddr();
|
replaceableSockAddr->~HifiSockAddr();
|
||||||
replaceableSockAddr = new (replaceableSockAddr) HifiSockAddr(iceServerHostname, ICE_SERVER_DEFAULT_PORT);
|
replaceableSockAddr = new (replaceableSockAddr) HifiSockAddr(iceServerHostname, ICE_SERVER_DEFAULT_PORT);
|
||||||
|
|
||||||
// refresh our ICE client UUID to something new
|
// refresh our ICE client UUID to something new
|
||||||
_iceClientID = QUuid::createUuid();
|
_iceClientID = QUuid::createUuid();
|
||||||
|
|
||||||
qCDebug(networking) << "ICE required to connect to domain via ice server at" << iceServerHostname;
|
qCDebug(networking) << "ICE required to connect to domain via ice server at" << iceServerHostname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,23 +145,29 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
|
||||||
void DomainHandler::activateICELocalSocket() {
|
void DomainHandler::activateICELocalSocket() {
|
||||||
_sockAddr = _icePeer.getLocalSocket();
|
_sockAddr = _icePeer.getLocalSocket();
|
||||||
_hostname = _sockAddr.getAddress().toString();
|
_hostname = _sockAddr.getAddress().toString();
|
||||||
|
emit completedSocketDiscovery();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainHandler::activateICEPublicSocket() {
|
void DomainHandler::activateICEPublicSocket() {
|
||||||
_sockAddr = _icePeer.getPublicSocket();
|
_sockAddr = _icePeer.getPublicSocket();
|
||||||
_hostname = _sockAddr.getAddress().toString();
|
_hostname = _sockAddr.getAddress().toString();
|
||||||
|
emit completedSocketDiscovery();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
|
void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
|
||||||
for (int i = 0; i < hostInfo.addresses().size(); i++) {
|
for (int i = 0; i < hostInfo.addresses().size(); i++) {
|
||||||
if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) {
|
||||||
_sockAddr.setAddress(hostInfo.addresses()[i]);
|
_sockAddr.setAddress(hostInfo.addresses()[i]);
|
||||||
|
|
||||||
qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(),
|
qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(),
|
||||||
_sockAddr.getAddress().toString().toLocal8Bit().constData());
|
_sockAddr.getAddress().toString().toLocal8Bit().constData());
|
||||||
|
|
||||||
|
emit completedSocketDiscovery();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we got here then we failed to lookup the address
|
// if we got here then we failed to lookup the address
|
||||||
qCDebug(networking, "Failed domain server lookup");
|
qCDebug(networking, "Failed domain server lookup");
|
||||||
}
|
}
|
||||||
|
@ -166,10 +175,10 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) {
|
||||||
void DomainHandler::setIsConnected(bool isConnected) {
|
void DomainHandler::setIsConnected(bool isConnected) {
|
||||||
if (_isConnected != isConnected) {
|
if (_isConnected != isConnected) {
|
||||||
_isConnected = isConnected;
|
_isConnected = isConnected;
|
||||||
|
|
||||||
if (_isConnected) {
|
if (_isConnected) {
|
||||||
emit connectedToDomain(_hostname);
|
emit connectedToDomain(_hostname);
|
||||||
|
|
||||||
// we've connected to new domain - time to ask it for global settings
|
// we've connected to new domain - time to ask it for global settings
|
||||||
requestDomainSettings();
|
requestDomainSettings();
|
||||||
} else {
|
} else {
|
||||||
|
@ -195,9 +204,9 @@ void DomainHandler::requestDomainSettings() {
|
||||||
settingsJSONURL.setPath("/settings.json");
|
settingsJSONURL.setPath("/settings.json");
|
||||||
Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get<NodeList>()->getOwnerType());
|
Assignment::Type assignmentType = Assignment::typeForNodeType(DependencyManager::get<NodeList>()->getOwnerType());
|
||||||
settingsJSONURL.setQuery(QString("type=%1").arg(assignmentType));
|
settingsJSONURL.setQuery(QString("type=%1").arg(assignmentType));
|
||||||
|
|
||||||
qCDebug(networking) << "Requesting domain-server settings at" << settingsJSONURL.toString();
|
qCDebug(networking) << "Requesting domain-server settings at" << settingsJSONURL.toString();
|
||||||
|
|
||||||
QNetworkRequest settingsRequest(settingsJSONURL);
|
QNetworkRequest settingsRequest(settingsJSONURL);
|
||||||
settingsRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
settingsRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||||
QNetworkReply* reply = NetworkAccessManager::getInstance().get(settingsRequest);
|
QNetworkReply* reply = NetworkAccessManager::getInstance().get(settingsRequest);
|
||||||
|
@ -210,23 +219,23 @@ const int MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS = 5;
|
||||||
|
|
||||||
void DomainHandler::settingsRequestFinished() {
|
void DomainHandler::settingsRequestFinished() {
|
||||||
QNetworkReply* settingsReply = reinterpret_cast<QNetworkReply*>(sender());
|
QNetworkReply* settingsReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||||
|
|
||||||
int replyCode = settingsReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
int replyCode = settingsReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
|
||||||
if (settingsReply->error() == QNetworkReply::NoError && replyCode != 301 && replyCode != 302) {
|
if (settingsReply->error() == QNetworkReply::NoError && replyCode != 301 && replyCode != 302) {
|
||||||
// parse the JSON to a QJsonObject and save it
|
// parse the JSON to a QJsonObject and save it
|
||||||
_settingsObject = QJsonDocument::fromJson(settingsReply->readAll()).object();
|
_settingsObject = QJsonDocument::fromJson(settingsReply->readAll()).object();
|
||||||
|
|
||||||
qCDebug(networking) << "Received domain settings.";
|
qCDebug(networking) << "Received domain settings.";
|
||||||
emit settingsReceived(_settingsObject);
|
emit settingsReceived(_settingsObject);
|
||||||
|
|
||||||
// reset failed settings requests to 0, we got them
|
// reset failed settings requests to 0, we got them
|
||||||
_failedSettingsRequests = 0;
|
_failedSettingsRequests = 0;
|
||||||
} else {
|
} else {
|
||||||
// error grabbing the settings - in some cases this means we are stuck
|
// error grabbing the settings - in some cases this means we are stuck
|
||||||
// so we should retry until we get it
|
// so we should retry until we get it
|
||||||
qCDebug(networking) << "Error getting domain settings -" << settingsReply->errorString() << "- retrying";
|
qCDebug(networking) << "Error getting domain settings -" << settingsReply->errorString() << "- retrying";
|
||||||
|
|
||||||
if (++_failedSettingsRequests >= MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS) {
|
if (++_failedSettingsRequests >= MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS) {
|
||||||
qCDebug(networking) << "Failed to retreive domain-server settings" << MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS
|
qCDebug(networking) << "Failed to retreive domain-server settings" << MAX_SETTINGS_REQUEST_FAILED_ATTEMPTS
|
||||||
<< "times. Re-setting connection to domain.";
|
<< "times. Re-setting connection to domain.";
|
||||||
|
@ -235,7 +244,7 @@ void DomainHandler::settingsRequestFinished() {
|
||||||
emit settingsReceiveFail();
|
emit settingsReceiveFail();
|
||||||
} else {
|
} else {
|
||||||
requestDomainSettings();
|
requestDomainSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settingsReply->deleteLater();
|
settingsReply->deleteLater();
|
||||||
}
|
}
|
||||||
|
@ -243,30 +252,30 @@ void DomainHandler::settingsRequestFinished() {
|
||||||
void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket) {
|
void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket) {
|
||||||
// figure out the port that the DS wants us to use for us to talk to them with DTLS
|
// figure out the port that the DS wants us to use for us to talk to them with DTLS
|
||||||
int numBytesPacketHeader = numBytesForPacketHeader(dtlsRequirementPacket);
|
int numBytesPacketHeader = numBytesForPacketHeader(dtlsRequirementPacket);
|
||||||
|
|
||||||
unsigned short dtlsPort = 0;
|
unsigned short dtlsPort = 0;
|
||||||
memcpy(&dtlsPort, dtlsRequirementPacket.data() + numBytesPacketHeader, sizeof(dtlsPort));
|
memcpy(&dtlsPort, dtlsRequirementPacket.data() + numBytesPacketHeader, sizeof(dtlsPort));
|
||||||
|
|
||||||
qCDebug(networking) << "domain-server DTLS port changed to" << dtlsPort << "- Enabling DTLS.";
|
qCDebug(networking) << "domain-server DTLS port changed to" << dtlsPort << "- Enabling DTLS.";
|
||||||
|
|
||||||
_sockAddr.setPort(dtlsPort);
|
_sockAddr.setPort(dtlsPort);
|
||||||
|
|
||||||
// initializeDTLSSession();
|
// initializeDTLSSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) {
|
void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) {
|
||||||
QDataStream iceResponseStream(icePacket);
|
QDataStream iceResponseStream(icePacket);
|
||||||
iceResponseStream.skipRawData(numBytesForPacketHeader(icePacket));
|
iceResponseStream.skipRawData(numBytesForPacketHeader(icePacket));
|
||||||
|
|
||||||
NetworkPeer packetPeer;
|
NetworkPeer packetPeer;
|
||||||
iceResponseStream >> packetPeer;
|
iceResponseStream >> packetPeer;
|
||||||
|
|
||||||
if (packetPeer.getUUID() != _iceDomainID) {
|
if (packetPeer.getUUID() != _iceDomainID) {
|
||||||
qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection.";
|
qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection.";
|
||||||
} else {
|
} else {
|
||||||
qCDebug(networking) << "Received network peer object for domain -" << packetPeer;
|
qCDebug(networking) << "Received network peer object for domain -" << packetPeer;
|
||||||
_icePeer = packetPeer;
|
_icePeer = packetPeer;
|
||||||
|
|
||||||
emit requestICEConnectionAttempt();
|
emit requestICEConnectionAttempt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,67 +31,79 @@ class DomainHandler : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
DomainHandler(QObject* parent = 0);
|
DomainHandler(QObject* parent = 0);
|
||||||
|
|
||||||
void clearConnectionInfo();
|
void clearConnectionInfo();
|
||||||
void clearSettings();
|
void clearSettings();
|
||||||
|
|
||||||
const QUuid& getUUID() const { return _uuid; }
|
const QUuid& getUUID() const { return _uuid; }
|
||||||
void setUUID(const QUuid& uuid);
|
void setUUID(const QUuid& uuid);
|
||||||
|
|
||||||
const QString& getHostname() const { return _hostname; }
|
const QString& getHostname() const { return _hostname; }
|
||||||
|
|
||||||
const QHostAddress& getIP() const { return _sockAddr.getAddress(); }
|
const QHostAddress& getIP() const { return _sockAddr.getAddress(); }
|
||||||
void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); }
|
void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); }
|
||||||
|
|
||||||
const HifiSockAddr& getSockAddr() { return _sockAddr; }
|
const HifiSockAddr& getSockAddr() { return _sockAddr; }
|
||||||
void setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname);
|
void setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname);
|
||||||
|
|
||||||
unsigned short getPort() const { return _sockAddr.getPort(); }
|
unsigned short getPort() const { return _sockAddr.getPort(); }
|
||||||
void setPort(quint16 port) { _sockAddr.setPort(port); }
|
void setPort(quint16 port) { _sockAddr.setPort(port); }
|
||||||
|
|
||||||
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
|
const QUuid& getAssignmentUUID() const { return _assignmentUUID; }
|
||||||
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
|
void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; }
|
||||||
|
|
||||||
const QUuid& getICEDomainID() const { return _iceDomainID; }
|
const QUuid& getICEDomainID() const { return _iceDomainID; }
|
||||||
|
|
||||||
const QUuid& getICEClientID() const { return _iceClientID; }
|
const QUuid& getICEClientID() const { return _iceClientID; }
|
||||||
|
|
||||||
bool requiresICE() const { return !_iceServerSockAddr.isNull(); }
|
bool requiresICE() const { return !_iceServerSockAddr.isNull(); }
|
||||||
const HifiSockAddr& getICEServerSockAddr() const { return _iceServerSockAddr; }
|
const HifiSockAddr& getICEServerSockAddr() const { return _iceServerSockAddr; }
|
||||||
NetworkPeer& getICEPeer() { return _icePeer; }
|
NetworkPeer& getICEPeer() { return _icePeer; }
|
||||||
void activateICELocalSocket();
|
void activateICELocalSocket();
|
||||||
void activateICEPublicSocket();
|
void activateICEPublicSocket();
|
||||||
|
|
||||||
bool isConnected() const { return _isConnected; }
|
bool isConnected() const { return _isConnected; }
|
||||||
void setIsConnected(bool isConnected);
|
void setIsConnected(bool isConnected);
|
||||||
|
|
||||||
bool hasSettings() const { return !_settingsObject.isEmpty(); }
|
bool hasSettings() const { return !_settingsObject.isEmpty(); }
|
||||||
void requestDomainSettings();
|
void requestDomainSettings();
|
||||||
const QJsonObject& getSettingsObject() const { return _settingsObject; }
|
const QJsonObject& getSettingsObject() const { return _settingsObject; }
|
||||||
|
|
||||||
void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket);
|
void parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket);
|
||||||
void processICEResponsePacket(const QByteArray& icePacket);
|
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();
|
void softReset();
|
||||||
public slots:
|
public slots:
|
||||||
void setHostnameAndPort(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT);
|
void setHostnameAndPort(const QString& hostname, quint16 port = DEFAULT_DOMAIN_SERVER_PORT);
|
||||||
void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id);
|
void setIceServerHostnameAndID(const QString& iceServerHostname, const QUuid& id);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void completedHostnameLookup(const QHostInfo& hostInfo);
|
void completedHostnameLookup(const QHostInfo& hostInfo);
|
||||||
void settingsRequestFinished();
|
void settingsRequestFinished();
|
||||||
signals:
|
signals:
|
||||||
void hostnameChanged(const QString& hostname);
|
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 connectedToDomain(const QString& hostname);
|
||||||
void disconnectedFromDomain();
|
void disconnectedFromDomain();
|
||||||
|
|
||||||
void requestICEConnectionAttempt();
|
void requestICEConnectionAttempt();
|
||||||
|
|
||||||
void settingsReceived(const QJsonObject& domainSettingsObject);
|
void settingsReceived(const QJsonObject& domainSettingsObject);
|
||||||
void settingsReceiveFail();
|
void settingsReceiveFail();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void hardReset();
|
void hardReset();
|
||||||
|
|
||||||
QUuid _uuid;
|
QUuid _uuid;
|
||||||
QString _hostname;
|
QString _hostname;
|
||||||
HifiSockAddr _sockAddr;
|
HifiSockAddr _sockAddr;
|
||||||
|
@ -103,6 +115,7 @@ private:
|
||||||
bool _isConnected;
|
bool _isConnected;
|
||||||
QJsonObject _settingsObject;
|
QJsonObject _settingsObject;
|
||||||
int _failedSettingsRequests;
|
int _failedSettingsRequests;
|
||||||
|
QString _pendingPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_DomainHandler_h
|
#endif // hifi_DomainHandler_h
|
||||||
|
|
|
@ -29,26 +29,27 @@ public:
|
||||||
HifiSockAddr(const HifiSockAddr& otherSockAddr);
|
HifiSockAddr(const HifiSockAddr& otherSockAddr);
|
||||||
HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup = false);
|
HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup = false);
|
||||||
HifiSockAddr(const sockaddr* sockaddr);
|
HifiSockAddr(const sockaddr* sockaddr);
|
||||||
|
|
||||||
bool isNull() const { return _address.isNull() && _port == 0; }
|
bool isNull() const { return _address.isNull() && _port == 0; }
|
||||||
|
void clear() { _address = QHostAddress::Null; _port = 0;}
|
||||||
|
|
||||||
HifiSockAddr& operator=(const HifiSockAddr& rhsSockAddr);
|
HifiSockAddr& operator=(const HifiSockAddr& rhsSockAddr);
|
||||||
void swap(HifiSockAddr& otherSockAddr);
|
void swap(HifiSockAddr& otherSockAddr);
|
||||||
|
|
||||||
bool operator==(const HifiSockAddr& rhsSockAddr) const;
|
bool operator==(const HifiSockAddr& rhsSockAddr) const;
|
||||||
bool operator!=(const HifiSockAddr& rhsSockAddr) const { return !(*this == rhsSockAddr); }
|
bool operator!=(const HifiSockAddr& rhsSockAddr) const { return !(*this == rhsSockAddr); }
|
||||||
|
|
||||||
const QHostAddress& getAddress() const { return _address; }
|
const QHostAddress& getAddress() const { return _address; }
|
||||||
QHostAddress* getAddressPointer() { return &_address; }
|
QHostAddress* getAddressPointer() { return &_address; }
|
||||||
void setAddress(const QHostAddress& address) { _address = address; }
|
void setAddress(const QHostAddress& address) { _address = address; }
|
||||||
|
|
||||||
quint16 getPort() const { return _port; }
|
quint16 getPort() const { return _port; }
|
||||||
quint16* getPortPointer() { return &_port; }
|
quint16* getPortPointer() { return &_port; }
|
||||||
void setPort(quint16 port) { _port = port; }
|
void setPort(quint16 port) { _port = port; }
|
||||||
|
|
||||||
static int packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr);
|
static int packSockAddr(unsigned char* packetData, const HifiSockAddr& packSockAddr);
|
||||||
static int unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr);
|
static int unpackSockAddr(const unsigned char* packetData, HifiSockAddr& unpackDestSockAddr);
|
||||||
|
|
||||||
friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr);
|
friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr);
|
||||||
friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr);
|
friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr);
|
||||||
friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);
|
friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);
|
||||||
|
|
|
@ -46,23 +46,30 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
|
||||||
firstCall = false;
|
firstCall = false;
|
||||||
}
|
}
|
||||||
auto addressManager = DependencyManager::get<AddressManager>();
|
auto addressManager = DependencyManager::get<AddressManager>();
|
||||||
|
|
||||||
// handle domain change signals from AddressManager
|
// handle domain change signals from AddressManager
|
||||||
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequired,
|
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequired,
|
||||||
&_domainHandler, &DomainHandler::setHostnameAndPort);
|
&_domainHandler, &DomainHandler::setHostnameAndPort);
|
||||||
|
|
||||||
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID,
|
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID,
|
||||||
&_domainHandler, &DomainHandler::setIceServerHostnameAndID);
|
&_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
|
// clear our NodeList when the domain changes
|
||||||
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset);
|
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset);
|
||||||
|
|
||||||
// handle ICE signal from DS so connection is attempted immediately
|
// handle ICE signal from DS so connection is attempted immediately
|
||||||
connect(&_domainHandler, &DomainHandler::requestICEConnectionAttempt, this, &NodeList::handleICEConnectionToDomainServer);
|
connect(&_domainHandler, &DomainHandler::requestICEConnectionAttempt, this, &NodeList::handleICEConnectionToDomainServer);
|
||||||
|
|
||||||
// clear out NodeList when login is finished
|
// clear out NodeList when login is finished
|
||||||
connect(&AccountManager::getInstance(), &AccountManager::loginComplete , this, &NodeList::reset);
|
connect(&AccountManager::getInstance(), &AccountManager::loginComplete , this, &NodeList::reset);
|
||||||
|
|
||||||
// clear our NodeList when logout is requested
|
// clear our NodeList when logout is requested
|
||||||
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
|
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
|
||||||
}
|
}
|
||||||
|
@ -75,8 +82,8 @@ qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& d
|
||||||
QStringList statsStringList = JSONBreakableMarshal::toStringList(statsObject, "");
|
QStringList statsStringList = JSONBreakableMarshal::toStringList(statsObject, "");
|
||||||
|
|
||||||
int numBytesWritten = numBytesForPacketHeader;
|
int numBytesWritten = numBytesForPacketHeader;
|
||||||
|
|
||||||
// enumerate the resulting strings - pack them and send off packets once we hit MTU size
|
// enumerate the resulting strings - pack them and send off packets once we hit MTU size
|
||||||
foreach(const QString& statsItem, statsStringList) {
|
foreach(const QString& statsItem, statsStringList) {
|
||||||
QByteArray utf8String = statsItem.toUtf8();
|
QByteArray utf8String = statsItem.toUtf8();
|
||||||
utf8String.append('\0');
|
utf8String.append('\0');
|
||||||
|
@ -85,14 +92,14 @@ qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& d
|
||||||
// send off the current packet since the next string will make us too big
|
// send off the current packet since the next string will make us too big
|
||||||
statsPacket.resize(numBytesWritten);
|
statsPacket.resize(numBytesWritten);
|
||||||
writeUnverifiedDatagram(statsPacket, destination);
|
writeUnverifiedDatagram(statsPacket, destination);
|
||||||
|
|
||||||
// reset the number of bytes written to the size of our packet header
|
// reset the number of bytes written to the size of our packet header
|
||||||
numBytesWritten = numBytesForPacketHeader;
|
numBytesWritten = numBytesForPacketHeader;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write this string into the stats packet
|
// write this string into the stats packet
|
||||||
statsPacket.replace(numBytesWritten, utf8String.size(), utf8String);
|
statsPacket.replace(numBytesWritten, utf8String.size(), utf8String);
|
||||||
|
|
||||||
// keep track of the number of bytes we have written
|
// keep track of the number of bytes we have written
|
||||||
numBytesWritten += utf8String.size();
|
numBytesWritten += utf8String.size();
|
||||||
}
|
}
|
||||||
|
@ -102,7 +109,7 @@ qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& d
|
||||||
statsPacket.resize(numBytesWritten);
|
statsPacket.resize(numBytesWritten);
|
||||||
writeUnverifiedDatagram(statsPacket, destination);
|
writeUnverifiedDatagram(statsPacket, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
// enumerate the resulting strings, breaking them into MTU sized packets
|
// enumerate the resulting strings, breaking them into MTU sized packets
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -114,26 +121,26 @@ qint64 NodeList::sendStatsToDomainServer(const QJsonObject& statsObject) {
|
||||||
void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode) {
|
void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode) {
|
||||||
QDataStream packetStream(packet);
|
QDataStream packetStream(packet);
|
||||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||||
|
|
||||||
quint8 pingType;
|
quint8 pingType;
|
||||||
quint64 ourOriginalTime, othersReplyTime;
|
quint64 ourOriginalTime, othersReplyTime;
|
||||||
|
|
||||||
packetStream >> pingType >> ourOriginalTime >> othersReplyTime;
|
packetStream >> pingType >> ourOriginalTime >> othersReplyTime;
|
||||||
|
|
||||||
quint64 now = usecTimestampNow();
|
quint64 now = usecTimestampNow();
|
||||||
int pingTime = now - ourOriginalTime;
|
int pingTime = now - ourOriginalTime;
|
||||||
int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
|
int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
|
||||||
|
|
||||||
// The other node's expected time should be our original time plus the one way flight time
|
// The other node's expected time should be our original time plus the one way flight time
|
||||||
// anything other than that is clock skew
|
// anything other than that is clock skew
|
||||||
quint64 othersExprectedReply = ourOriginalTime + oneWayFlightTime;
|
quint64 othersExprectedReply = ourOriginalTime + oneWayFlightTime;
|
||||||
int clockSkew = othersReplyTime - othersExprectedReply;
|
int clockSkew = othersReplyTime - othersExprectedReply;
|
||||||
|
|
||||||
sendingNode->setPingMs(pingTime / 1000);
|
sendingNode->setPingMs(pingTime / 1000);
|
||||||
sendingNode->updateClockSkewUsec(clockSkew);
|
sendingNode->updateClockSkewUsec(clockSkew);
|
||||||
|
|
||||||
const bool wantDebug = false;
|
const bool wantDebug = false;
|
||||||
|
|
||||||
if (wantDebug) {
|
if (wantDebug) {
|
||||||
qCDebug(networking) << "PING_REPLY from node " << *sendingNode << "\n" <<
|
qCDebug(networking) << "PING_REPLY from node " << *sendingNode << "\n" <<
|
||||||
" now: " << now << "\n" <<
|
" now: " << now << "\n" <<
|
||||||
|
@ -172,7 +179,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
||||||
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||||
QByteArray replyPacket = constructPingReplyPacket(packet);
|
QByteArray replyPacket = constructPingReplyPacket(packet);
|
||||||
writeDatagram(replyPacket, matchingNode, senderSockAddr);
|
writeDatagram(replyPacket, matchingNode, senderSockAddr);
|
||||||
|
|
||||||
// If we don't have a symmetric socket for this node and this socket doesn't match
|
// If we don't have a symmetric socket for this node and this socket doesn't match
|
||||||
// what we have for public and local then set it as the symmetric.
|
// what we have for public and local then set it as the symmetric.
|
||||||
// This allows a server on a reachable port to communicate with nodes on symmetric NATs
|
// This allows a server on a reachable port to communicate with nodes on symmetric NATs
|
||||||
|
@ -182,7 +189,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PacketTypePingReply: {
|
case PacketTypePingReply: {
|
||||||
|
@ -190,14 +197,14 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
||||||
|
|
||||||
if (sendingNode) {
|
if (sendingNode) {
|
||||||
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
|
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
|
||||||
|
|
||||||
// activate the appropriate socket for this node, if not yet updated
|
// activate the appropriate socket for this node, if not yet updated
|
||||||
activateSocketFromNodeCommunication(packet, sendingNode);
|
activateSocketFromNodeCommunication(packet, sendingNode);
|
||||||
|
|
||||||
// set the ping time for this node for stat collection
|
// set the ping time for this node for stat collection
|
||||||
timePingReply(packet, sendingNode);
|
timePingReply(packet, sendingNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PacketTypeUnverifiedPing: {
|
case PacketTypeUnverifiedPing: {
|
||||||
|
@ -208,7 +215,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
||||||
}
|
}
|
||||||
case PacketTypeUnverifiedPingReply: {
|
case PacketTypeUnverifiedPingReply: {
|
||||||
qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr;
|
qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr;
|
||||||
|
|
||||||
// for now we're unsafely assuming this came back from the domain
|
// for now we're unsafely assuming this came back from the domain
|
||||||
if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) {
|
if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) {
|
||||||
qCDebug(networking) << "Connecting to domain using local socket";
|
qCDebug(networking) << "Connecting to domain using local socket";
|
||||||
|
@ -226,6 +233,10 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
||||||
processSTUNResponse(packet);
|
processSTUNResponse(packet);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PacketTypeDomainServerPathResponse: {
|
||||||
|
handleDSPathQueryResponse(packet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
LimitedNodeList::processNodeData(senderSockAddr, packet);
|
LimitedNodeList::processNodeData(senderSockAddr, packet);
|
||||||
break;
|
break;
|
||||||
|
@ -234,17 +245,17 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
|
||||||
|
|
||||||
void NodeList::reset() {
|
void NodeList::reset() {
|
||||||
LimitedNodeList::reset();
|
LimitedNodeList::reset();
|
||||||
|
|
||||||
_numNoReplyDomainCheckIns = 0;
|
_numNoReplyDomainCheckIns = 0;
|
||||||
|
|
||||||
// refresh the owner UUID to the NULL UUID
|
// refresh the owner UUID to the NULL UUID
|
||||||
setSessionUUID(QUuid());
|
setSessionUUID(QUuid());
|
||||||
|
|
||||||
if (sender() != &_domainHandler) {
|
if (sender() != &_domainHandler) {
|
||||||
// clear the domain connection information, unless they're the ones that asked us to reset
|
// clear the domain connection information, unless they're the ones that asked us to reset
|
||||||
_domainHandler.softReset();
|
_domainHandler.softReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we setup the DTLS socket, also disconnect from the DTLS socket readyRead() so it can handle handshaking
|
// if we setup the DTLS socket, also disconnect from the DTLS socket readyRead() so it can handle handshaking
|
||||||
if (_dtlsSocket) {
|
if (_dtlsSocket) {
|
||||||
disconnect(_dtlsSocket, 0, this, 0);
|
disconnect(_dtlsSocket, 0, this, 0);
|
||||||
|
@ -267,7 +278,7 @@ void NodeList::sendSTUNRequest() {
|
||||||
if (!_hasCompletedInitialSTUNFailure) {
|
if (!_hasCompletedInitialSTUNFailure) {
|
||||||
qCDebug(networking) << "Sending intial stun request to" << STUN_SERVER_HOSTNAME;
|
qCDebug(networking) << "Sending intial stun request to" << STUN_SERVER_HOSTNAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
LimitedNodeList::sendSTUNRequest();
|
LimitedNodeList::sendSTUNRequest();
|
||||||
|
|
||||||
_stunRequestsSinceSuccess++;
|
_stunRequestsSinceSuccess++;
|
||||||
|
@ -292,9 +303,9 @@ bool NodeList::processSTUNResponse(const QByteArray& packet) {
|
||||||
if (LimitedNodeList::processSTUNResponse(packet)) {
|
if (LimitedNodeList::processSTUNResponse(packet)) {
|
||||||
// reset the number of failed STUN requests since last success
|
// reset the number of failed STUN requests since last success
|
||||||
_stunRequestsSinceSuccess = 0;
|
_stunRequestsSinceSuccess = 0;
|
||||||
|
|
||||||
_hasCompletedInitialSTUNFailure = true;
|
_hasCompletedInitialSTUNFailure = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -309,23 +320,23 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
} else if (_domainHandler.getIP().isNull() && _domainHandler.requiresICE()) {
|
} else if (_domainHandler.getIP().isNull() && _domainHandler.requiresICE()) {
|
||||||
handleICEConnectionToDomainServer();
|
handleICEConnectionToDomainServer();
|
||||||
} else if (!_domainHandler.getIP().isNull()) {
|
} else if (!_domainHandler.getIP().isNull()) {
|
||||||
|
|
||||||
bool isUsingDTLS = false;
|
bool isUsingDTLS = false;
|
||||||
|
|
||||||
PacketType domainPacketType = !_domainHandler.isConnected()
|
PacketType domainPacketType = !_domainHandler.isConnected()
|
||||||
? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
|
? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
|
||||||
|
|
||||||
if (!_domainHandler.isConnected()) {
|
if (!_domainHandler.isConnected()) {
|
||||||
qCDebug(networking) << "Sending connect request to domain-server at" << _domainHandler.getHostname();
|
qCDebug(networking) << "Sending connect request to domain-server at" << _domainHandler.getHostname();
|
||||||
|
|
||||||
// is this our localhost domain-server?
|
// is this our localhost domain-server?
|
||||||
// if so we need to make sure we have an up-to-date local port in case it restarted
|
// if so we need to make sure we have an up-to-date local port in case it restarted
|
||||||
|
|
||||||
if (_domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost
|
if (_domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost
|
||||||
|| _domainHandler.getHostname() == "localhost") {
|
|| _domainHandler.getHostname() == "localhost") {
|
||||||
|
|
||||||
static QSharedMemory* localDSPortSharedMem = NULL;
|
static QSharedMemory* localDSPortSharedMem = NULL;
|
||||||
|
|
||||||
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
|
||||||
getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY,
|
getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY,
|
||||||
localDSPortSharedMem,
|
localDSPortSharedMem,
|
||||||
|
@ -333,12 +344,12 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
qCDebug(networking) << "Local domain-server port read from shared memory (or default) is" << domainPort;
|
qCDebug(networking) << "Local domain-server port read from shared memory (or default) is" << domainPort;
|
||||||
_domainHandler.setPort(domainPort);
|
_domainHandler.setPort(domainPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// construct the DS check in packet
|
// construct the DS check in packet
|
||||||
QUuid packetUUID = _sessionUUID;
|
QUuid packetUUID = _sessionUUID;
|
||||||
|
|
||||||
if (domainPacketType == PacketTypeDomainConnectRequest) {
|
if (domainPacketType == PacketTypeDomainConnectRequest) {
|
||||||
if (!_domainHandler.getAssignmentUUID().isNull()) {
|
if (!_domainHandler.getAssignmentUUID().isNull()) {
|
||||||
// this is a connect request and we're an assigned node
|
// this is a connect request and we're an assigned node
|
||||||
|
@ -351,70 +362,166 @@ void NodeList::sendDomainServerCheckIn() {
|
||||||
packetUUID = _domainHandler.getICEClientID();
|
packetUUID = _domainHandler.getICEClientID();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray domainServerPacket = byteArrayWithUUIDPopulatedHeader(domainPacketType, packetUUID);
|
QByteArray domainServerPacket = byteArrayWithUUIDPopulatedHeader(domainPacketType, packetUUID);
|
||||||
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
|
||||||
|
|
||||||
// pack our data to send to the domain-server
|
// pack our data to send to the domain-server
|
||||||
packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList();
|
packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList();
|
||||||
|
|
||||||
|
|
||||||
// if this is a connect request, and we can present a username signature, send it along
|
// if this is a connect request, and we can present a username signature, send it along
|
||||||
if (!_domainHandler.isConnected()) {
|
if (!_domainHandler.isConnected()) {
|
||||||
DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo();
|
DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo();
|
||||||
packetStream << accountInfo.getUsername();
|
packetStream << accountInfo.getUsername();
|
||||||
|
|
||||||
const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature();
|
const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature();
|
||||||
|
|
||||||
if (!usernameSignature.isEmpty()) {
|
if (!usernameSignature.isEmpty()) {
|
||||||
qCDebug(networking) << "Including username signature in domain connect request.";
|
qCDebug(networking) << "Including username signature in domain connect request.";
|
||||||
packetStream << usernameSignature;
|
packetStream << usernameSignature;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isUsingDTLS) {
|
if (!isUsingDTLS) {
|
||||||
writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr());
|
writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr());
|
||||||
}
|
}
|
||||||
|
|
||||||
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
|
||||||
static unsigned int numDomainCheckins = 0;
|
static unsigned int numDomainCheckins = 0;
|
||||||
|
|
||||||
// send a STUN request every Nth domain server check in so we update our public socket, if required
|
// send a STUN request every Nth domain server check in so we update our public socket, if required
|
||||||
if (numDomainCheckins++ % NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST == 0) {
|
if (numDomainCheckins++ % NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST == 0) {
|
||||||
sendSTUNRequest();
|
sendSTUNRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
|
||||||
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
|
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
|
||||||
// so emit our signal that says that
|
// so emit our signal that says that
|
||||||
emit limitOfSilentDomainCheckInsReached();
|
emit limitOfSilentDomainCheckInsReached();
|
||||||
}
|
}
|
||||||
|
|
||||||
// increment the count of un-replied check-ins
|
// increment the count of un-replied check-ins
|
||||||
_numNoReplyDomainCheckIns++;
|
_numNoReplyDomainCheckIns++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
void NodeList::handleICEConnectionToDomainServer() {
|
||||||
if (_domainHandler.getICEPeer().isNull()
|
if (_domainHandler.getICEPeer().isNull()
|
||||||
|| _domainHandler.getICEPeer().getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) {
|
|| _domainHandler.getICEPeer().getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) {
|
||||||
|
|
||||||
_domainHandler.getICEPeer().resetConnectionAttemps();
|
_domainHandler.getICEPeer().resetConnectionAttemps();
|
||||||
|
|
||||||
LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(),
|
LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(),
|
||||||
_domainHandler.getICEClientID(),
|
_domainHandler.getICEClientID(),
|
||||||
_domainHandler.getICEDomainID());
|
_domainHandler.getICEDomainID());
|
||||||
} else {
|
} else {
|
||||||
qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID"
|
qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID"
|
||||||
<< uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID());
|
<< uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID());
|
||||||
|
|
||||||
// send the ping packet to the local and public sockets for this node
|
// send the ping packet to the local and public sockets for this node
|
||||||
QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID());
|
QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID());
|
||||||
writeUnverifiedDatagram(localPingPacket, _domainHandler.getICEPeer().getLocalSocket());
|
writeUnverifiedDatagram(localPingPacket, _domainHandler.getICEPeer().getLocalSocket());
|
||||||
|
|
||||||
QByteArray publicPingPacket = constructPingPacket(PingType::Public, false, _domainHandler.getICEClientID());
|
QByteArray publicPingPacket = constructPingPacket(PingType::Public, false, _domainHandler.getICEClientID());
|
||||||
writeUnverifiedDatagram(publicPingPacket, _domainHandler.getICEPeer().getPublicSocket());
|
writeUnverifiedDatagram(publicPingPacket, _domainHandler.getICEPeer().getPublicSocket());
|
||||||
|
|
||||||
_domainHandler.getICEPeer().incrementConnectionAttempts();
|
_domainHandler.getICEPeer().incrementConnectionAttempts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -422,18 +529,18 @@ void NodeList::handleICEConnectionToDomainServer() {
|
||||||
int NodeList::processDomainServerList(const QByteArray& packet) {
|
int NodeList::processDomainServerList(const QByteArray& packet) {
|
||||||
// this is a packet from the domain server, reset the count of un-replied check-ins
|
// this is a packet from the domain server, reset the count of un-replied check-ins
|
||||||
_numNoReplyDomainCheckIns = 0;
|
_numNoReplyDomainCheckIns = 0;
|
||||||
|
|
||||||
// if this was the first domain-server list from this domain, we've now connected
|
// if this was the first domain-server list from this domain, we've now connected
|
||||||
if (!_domainHandler.isConnected()) {
|
if (!_domainHandler.isConnected()) {
|
||||||
_domainHandler.setUUID(uuidFromPacketHeader(packet));
|
_domainHandler.setUUID(uuidFromPacketHeader(packet));
|
||||||
_domainHandler.setIsConnected(true);
|
_domainHandler.setIsConnected(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int readNodes = 0;
|
int readNodes = 0;
|
||||||
|
|
||||||
QDataStream packetStream(packet);
|
QDataStream packetStream(packet);
|
||||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||||
|
|
||||||
// pull our owner UUID from the packet, it's always the first thing
|
// pull our owner UUID from the packet, it's always the first thing
|
||||||
QUuid newUUID;
|
QUuid newUUID;
|
||||||
packetStream >> newUUID;
|
packetStream >> newUUID;
|
||||||
|
@ -446,7 +553,7 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
|
||||||
bool thisNodeCanRez;
|
bool thisNodeCanRez;
|
||||||
packetStream >> thisNodeCanRez;
|
packetStream >> thisNodeCanRez;
|
||||||
setThisNodeCanRez(thisNodeCanRez);
|
setThisNodeCanRez(thisNodeCanRez);
|
||||||
|
|
||||||
// pull each node in the packet
|
// pull each node in the packet
|
||||||
while(packetStream.device()->pos() < packet.size()) {
|
while(packetStream.device()->pos() < packet.size()) {
|
||||||
// setup variables to read into from QDataStream
|
// setup variables to read into from QDataStream
|
||||||
|
@ -466,11 +573,11 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
|
||||||
|
|
||||||
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
|
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
|
||||||
nodeLocalSocket, canAdjustLocks, canRez);
|
nodeLocalSocket, canAdjustLocks, canRez);
|
||||||
|
|
||||||
packetStream >> connectionUUID;
|
packetStream >> connectionUUID;
|
||||||
node->setConnectionSecret(connectionUUID);
|
node->setConnectionSecret(connectionUUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ping inactive nodes in conjunction with receipt of list from domain-server
|
// ping inactive nodes in conjunction with receipt of list from domain-server
|
||||||
// this makes it happen every second and also pings any newly added nodes
|
// this makes it happen every second and also pings any newly added nodes
|
||||||
pingInactiveNodes();
|
pingInactiveNodes();
|
||||||
|
@ -479,25 +586,25 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::sendAssignment(Assignment& assignment) {
|
void NodeList::sendAssignment(Assignment& assignment) {
|
||||||
|
|
||||||
PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand
|
PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand
|
||||||
? PacketTypeCreateAssignment
|
? PacketTypeCreateAssignment
|
||||||
: PacketTypeRequestAssignment;
|
: PacketTypeRequestAssignment;
|
||||||
|
|
||||||
QByteArray packet = byteArrayWithPopulatedHeader(assignmentPacketType);
|
QByteArray packet = byteArrayWithPopulatedHeader(assignmentPacketType);
|
||||||
QDataStream packetStream(&packet, QIODevice::Append);
|
QDataStream packetStream(&packet, QIODevice::Append);
|
||||||
|
|
||||||
packetStream << assignment;
|
packetStream << assignment;
|
||||||
|
|
||||||
_nodeSocket.writeDatagram(packet, _assignmentServerSocket.getAddress(), _assignmentServerSocket.getPort());
|
_nodeSocket.writeDatagram(packet, _assignmentServerSocket.getAddress(), _assignmentServerSocket.getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
|
void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
|
||||||
|
|
||||||
// send the ping packet to the local and public sockets for this node
|
// send the ping packet to the local and public sockets for this node
|
||||||
QByteArray localPingPacket = constructPingPacket(PingType::Local);
|
QByteArray localPingPacket = constructPingPacket(PingType::Local);
|
||||||
writeDatagram(localPingPacket, node, node->getLocalSocket());
|
writeDatagram(localPingPacket, node, node->getLocalSocket());
|
||||||
|
|
||||||
QByteArray publicPingPacket = constructPingPacket(PingType::Public);
|
QByteArray publicPingPacket = constructPingPacket(PingType::Public);
|
||||||
writeDatagram(publicPingPacket, node, node->getPublicSocket());
|
writeDatagram(publicPingPacket, node, node->getPublicSocket());
|
||||||
|
|
||||||
|
@ -520,10 +627,10 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con
|
||||||
// deconstruct this ping packet to see if it is a public or local reply
|
// deconstruct this ping packet to see if it is a public or local reply
|
||||||
QDataStream packetStream(packet);
|
QDataStream packetStream(packet);
|
||||||
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
packetStream.skipRawData(numBytesForPacketHeader(packet));
|
||||||
|
|
||||||
quint8 pingType;
|
quint8 pingType;
|
||||||
packetStream >> pingType;
|
packetStream >> pingType;
|
||||||
|
|
||||||
// if this is a local or public ping then we can activate a socket
|
// if this is a local or public ping then we can activate a socket
|
||||||
// we do nothing with agnostic pings, those are simply for timing
|
// we do nothing with agnostic pings, those are simply for timing
|
||||||
if (pingType == PingType::Local && sendingNode->getActiveSocket() != &sendingNode->getLocalSocket()) {
|
if (pingType == PingType::Local && sendingNode->getActiveSocket() != &sendingNode->getLocalSocket()) {
|
||||||
|
|
|
@ -43,7 +43,7 @@ class Assignment;
|
||||||
class NodeList : public LimitedNodeList {
|
class NodeList : public LimitedNodeList {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NodeType_t getOwnerType() const { return _ownerType; }
|
NodeType_t getOwnerType() const { return _ownerType; }
|
||||||
void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; }
|
void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; }
|
||||||
|
@ -53,42 +53,49 @@ public:
|
||||||
|
|
||||||
int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; }
|
int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; }
|
||||||
DomainHandler& getDomainHandler() { return _domainHandler; }
|
DomainHandler& getDomainHandler() { return _domainHandler; }
|
||||||
|
|
||||||
const NodeSet& getNodeInterestSet() const { return _nodeTypesOfInterest; }
|
const NodeSet& getNodeInterestSet() const { return _nodeTypesOfInterest; }
|
||||||
void addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd);
|
void addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd);
|
||||||
void addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes);
|
void addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes);
|
||||||
void resetNodeInterestSet() { _nodeTypesOfInterest.clear(); }
|
void resetNodeInterestSet() { _nodeTypesOfInterest.clear(); }
|
||||||
|
|
||||||
void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
|
void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
|
||||||
|
|
||||||
int processDomainServerList(const QByteArray& packet);
|
int processDomainServerList(const QByteArray& packet);
|
||||||
|
|
||||||
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
|
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
|
||||||
void sendAssignment(Assignment& assignment);
|
void sendAssignment(Assignment& assignment);
|
||||||
|
|
||||||
void pingPunchForInactiveNode(const SharedNodePointer& node);
|
void pingPunchForInactiveNode(const SharedNodePointer& node);
|
||||||
public slots:
|
public slots:
|
||||||
void reset();
|
void reset();
|
||||||
void sendDomainServerCheckIn();
|
void sendDomainServerCheckIn();
|
||||||
void pingInactiveNodes();
|
void pingInactiveNodes();
|
||||||
|
void handleDSPathQuery(const QString& newPath);
|
||||||
signals:
|
signals:
|
||||||
void limitOfSilentDomainCheckInsReached();
|
void limitOfSilentDomainCheckInsReached();
|
||||||
|
private slots:
|
||||||
|
void sendPendingDSPathQuery();
|
||||||
private:
|
private:
|
||||||
NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile
|
NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile
|
||||||
NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
|
NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
|
||||||
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||||
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
|
||||||
|
|
||||||
void sendSTUNRequest();
|
void sendSTUNRequest();
|
||||||
bool processSTUNResponse(const QByteArray& packet);
|
bool processSTUNResponse(const QByteArray& packet);
|
||||||
|
|
||||||
void handleICEConnectionToDomainServer();
|
void handleICEConnectionToDomainServer();
|
||||||
|
|
||||||
void processDomainServerAuthRequest(const QByteArray& packet);
|
void processDomainServerAuthRequest(const QByteArray& packet);
|
||||||
void requestAuthForDomainServer();
|
void requestAuthForDomainServer();
|
||||||
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
|
||||||
void timePingReply(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;
|
NodeType_t _ownerType;
|
||||||
NodeSet _nodeTypesOfInterest;
|
NodeSet _nodeTypesOfInterest;
|
||||||
DomainHandler _domainHandler;
|
DomainHandler _domainHandler;
|
||||||
|
@ -96,7 +103,7 @@ private:
|
||||||
HifiSockAddr _assignmentServerSocket;
|
HifiSockAddr _assignmentServerSocket;
|
||||||
bool _hasCompletedInitialSTUNFailure;
|
bool _hasCompletedInitialSTUNFailure;
|
||||||
unsigned int _stunRequestsSinceSuccess;
|
unsigned int _stunRequestsSinceSuccess;
|
||||||
|
|
||||||
friend class Application;
|
friend class Application;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
return 1;
|
return 1;
|
||||||
case PacketTypeEntityAddOrEdit:
|
case PacketTypeEntityAddOrEdit:
|
||||||
case PacketTypeEntityData:
|
case PacketTypeEntityData:
|
||||||
return VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES;
|
return VERSION_ENTITIES_HAVE_LINE_TYPE;
|
||||||
case PacketTypeEntityErase:
|
case PacketTypeEntityErase:
|
||||||
return 2;
|
return 2;
|
||||||
case PacketTypeAudioStreamStats:
|
case PacketTypeAudioStreamStats:
|
||||||
|
|
|
@ -47,8 +47,8 @@ enum PacketType {
|
||||||
PacketTypeMuteEnvironment,
|
PacketTypeMuteEnvironment,
|
||||||
PacketTypeAudioStreamStats,
|
PacketTypeAudioStreamStats,
|
||||||
PacketTypeDataServerConfirm, // 20
|
PacketTypeDataServerConfirm, // 20
|
||||||
UNUSED_1,
|
PacketTypeDomainServerPathQuery,
|
||||||
UNUSED_2,
|
PacketTypeDomainServerPathResponse,
|
||||||
UNUSED_3,
|
UNUSED_3,
|
||||||
UNUSED_4,
|
UNUSED_4,
|
||||||
UNUSED_5, // 25
|
UNUSED_5, // 25
|
||||||
|
@ -96,7 +96,8 @@ const QSet<PacketType> NON_VERIFIED_PACKETS = QSet<PacketType>()
|
||||||
<< PacketTypeNodeJsonStats << PacketTypeEntityQuery
|
<< PacketTypeNodeJsonStats << PacketTypeEntityQuery
|
||||||
<< PacketTypeOctreeDataNack << PacketTypeEntityEditNack
|
<< PacketTypeOctreeDataNack << PacketTypeEntityEditNack
|
||||||
<< PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse
|
<< PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse
|
||||||
<< PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode;
|
<< PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode
|
||||||
|
<< PacketTypeDomainServerPathQuery << PacketTypeDomainServerPathResponse;
|
||||||
|
|
||||||
const QSet<PacketType> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType>()
|
const QSet<PacketType> SEQUENCE_NUMBERED_PACKETS = QSet<PacketType>()
|
||||||
<< PacketTypeAvatarData;
|
<< PacketTypeAvatarData;
|
||||||
|
@ -122,7 +123,7 @@ int numSequenceNumberBytesForType(PacketType packetType);
|
||||||
|
|
||||||
int numBytesForPacketHeader(const QByteArray& packet);
|
int numBytesForPacketHeader(const QByteArray& packet);
|
||||||
int numBytesForPacketHeader(const char* packet);
|
int numBytesForPacketHeader(const char* packet);
|
||||||
int numBytesForArithmeticCodedPacketType(PacketType packetType);
|
int numBytesForArithmeticCodedPacketType(PacketType packetType);
|
||||||
int numBytesForPacketHeaderGivenPacketType(PacketType packetType);
|
int numBytesForPacketHeaderGivenPacketType(PacketType packetType);
|
||||||
|
|
||||||
QUuid uuidFromPacketHeader(const QByteArray& packet);
|
QUuid uuidFromPacketHeader(const QByteArray& packet);
|
||||||
|
@ -138,9 +139,9 @@ QByteArray hashForPacketAndConnectionUUID(const QByteArray& packet, const QUuid&
|
||||||
|
|
||||||
PacketSequenceNumber sequenceNumberFromHeader(const QByteArray& packet, PacketType packetType = PacketTypeUnknown);
|
PacketSequenceNumber sequenceNumberFromHeader(const QByteArray& packet, PacketType packetType = PacketTypeUnknown);
|
||||||
|
|
||||||
void replaceHashInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketType packetType = PacketTypeUnknown);
|
void replaceHashInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketType packetType = PacketTypeUnknown);
|
||||||
|
|
||||||
void replaceSequenceNumberInPacket(QByteArray& packet, PacketSequenceNumber sequenceNumber,
|
void replaceSequenceNumberInPacket(QByteArray& packet, PacketSequenceNumber sequenceNumber,
|
||||||
PacketType packetType = PacketTypeUnknown);
|
PacketType packetType = PacketTypeUnknown);
|
||||||
|
|
||||||
void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber,
|
void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& connectionUUID, PacketSequenceNumber sequenceNumber,
|
||||||
|
@ -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_HAVE_SKYBOX = 21;
|
||||||
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_STAGE_HAS_AUTOMATIC_HOURDAY = 22;
|
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_STAGE_HAS_AUTOMATIC_HOURDAY = 22;
|
||||||
const PacketVersion VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES = 23;
|
const PacketVersion VERSION_ENTITIES_PARTICLE_ENTITIES_HAVE_TEXTURES = 23;
|
||||||
|
const PacketVersion VERSION_ENTITIES_HAVE_LINE_TYPE = 24;
|
||||||
|
|
||||||
#endif // hifi_PacketHeaders_h
|
#endif // hifi_PacketHeaders_h
|
||||||
|
|
|
@ -72,7 +72,7 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL
|
||||||
int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION);
|
int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION);
|
||||||
|
|
||||||
QString configFilePath;
|
QString configFilePath;
|
||||||
|
|
||||||
if (configIndex != -1) {
|
if (configIndex != -1) {
|
||||||
// we have a config file - try and read it
|
// we have a config file - try and read it
|
||||||
configFilePath = argumentList[configIndex + 1];
|
configFilePath = argumentList[configIndex + 1];
|
||||||
|
@ -82,8 +82,8 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL
|
||||||
QCoreApplication::organizationName(),
|
QCoreApplication::organizationName(),
|
||||||
QCoreApplication::applicationName());
|
QCoreApplication::applicationName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return mergedMap;
|
return mergedMap;
|
||||||
}
|
}
|
||||||
|
@ -94,23 +94,23 @@ HifiConfigVariantMap::HifiConfigVariantMap() :
|
||||||
_userConfig(),
|
_userConfig(),
|
||||||
_mergedConfig()
|
_mergedConfig()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentList) {
|
void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentList) {
|
||||||
// check if there is a master config file
|
// check if there is a master config file
|
||||||
const QString MASTER_CONFIG_FILE_OPTION = "--master-config";
|
const QString MASTER_CONFIG_FILE_OPTION = "--master-config";
|
||||||
|
|
||||||
int masterConfigIndex = argumentList.indexOf(MASTER_CONFIG_FILE_OPTION);
|
int masterConfigIndex = argumentList.indexOf(MASTER_CONFIG_FILE_OPTION);
|
||||||
if (masterConfigIndex != -1) {
|
if (masterConfigIndex != -1) {
|
||||||
QString masterConfigFilepath = argumentList[masterConfigIndex + 1];
|
QString masterConfigFilepath = argumentList[masterConfigIndex + 1];
|
||||||
|
|
||||||
loadMapFromJSONFile(_masterConfig, masterConfigFilepath);
|
loadMapFromJSONFile(_masterConfig, masterConfigFilepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// load the user config
|
// load the user config
|
||||||
const QString USER_CONFIG_FILE_OPTION = "--user-config";
|
const QString USER_CONFIG_FILE_OPTION = "--user-config";
|
||||||
|
|
||||||
int userConfigIndex = argumentList.indexOf(USER_CONFIG_FILE_OPTION);
|
int userConfigIndex = argumentList.indexOf(USER_CONFIG_FILE_OPTION);
|
||||||
if (userConfigIndex != -1) {
|
if (userConfigIndex != -1) {
|
||||||
_userConfigFilename = argumentList[userConfigIndex + 1];
|
_userConfigFilename = argumentList[userConfigIndex + 1];
|
||||||
|
@ -119,26 +119,26 @@ void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentLi
|
||||||
QCoreApplication::organizationName(),
|
QCoreApplication::organizationName(),
|
||||||
QCoreApplication::applicationName());
|
QCoreApplication::applicationName());
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMapFromJSONFile(_userConfig, _userConfigFilename);
|
loadMapFromJSONFile(_userConfig, _userConfigFilename);
|
||||||
|
|
||||||
// the merged config is initially matched to the master config
|
// the merged config is initially matched to the master config
|
||||||
_mergedConfig = _masterConfig;
|
_mergedConfig = _masterConfig;
|
||||||
|
|
||||||
// then we merge in anything missing from the user config
|
// then we merge in anything missing from the user config
|
||||||
addMissingValuesToExistingMap(_mergedConfig, _userConfig);
|
addMissingValuesToExistingMap(_mergedConfig, _userConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HifiConfigVariantMap::loadMapFromJSONFile(QVariantMap& existingMap, const QString& filename) {
|
void HifiConfigVariantMap::loadMapFromJSONFile(QVariantMap& existingMap, const QString& filename) {
|
||||||
QFile configFile(filename);
|
QFile configFile(filename);
|
||||||
|
|
||||||
if (configFile.exists()) {
|
if (configFile.exists()) {
|
||||||
qCDebug(shared) << "Reading JSON config file at" << filename;
|
qCDebug(shared) << "Reading JSON config file at" << filename;
|
||||||
configFile.open(QIODevice::ReadOnly);
|
configFile.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
|
QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll());
|
||||||
existingMap = configDocument.toVariant().toMap();
|
existingMap = configDocument.toVariant().toMap();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qCDebug(shared) << "Could not find JSON config file at" << filename;
|
qCDebug(shared) << "Could not find JSON config file at" << filename;
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ void HifiConfigVariantMap::addMissingValuesToExistingMap(QVariantMap& existingMa
|
||||||
foreach(const QString& key, newMap.keys()) {
|
foreach(const QString& key, newMap.keys()) {
|
||||||
if (existingMap.contains(key)) {
|
if (existingMap.contains(key)) {
|
||||||
// if this is just a regular value, we're done - we don't ovveride
|
// if this is just a regular value, we're done - we don't ovveride
|
||||||
|
|
||||||
if (newMap[key].canConvert(QMetaType::QVariantMap) && existingMap[key].canConvert(QMetaType::QVariantMap)) {
|
if (newMap[key].canConvert(QMetaType::QVariantMap) && existingMap[key].canConvert(QMetaType::QVariantMap)) {
|
||||||
// there's a variant map below and the existing map has one too, so we need to keep recursing
|
// there's a variant map below and the existing map has one too, so we need to keep recursing
|
||||||
addMissingValuesToExistingMap(*static_cast<QVariantMap*>(existingMap[key].data()), newMap[key].toMap());
|
addMissingValuesToExistingMap(*static_cast<QVariantMap*>(existingMap[key].data()), newMap[key].toMap());
|
||||||
|
@ -159,11 +159,11 @@ void HifiConfigVariantMap::addMissingValuesToExistingMap(QVariantMap& existingMa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath) {
|
QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath) {
|
||||||
int dotIndex = keyPath.indexOf('.');
|
int dotIndex = keyPath.indexOf('.');
|
||||||
|
|
||||||
QString firstKey = (dotIndex == -1) ? keyPath : keyPath.mid(0, dotIndex);
|
QString firstKey = (dotIndex == -1) ? keyPath : keyPath.mid(0, dotIndex);
|
||||||
|
|
||||||
if (variantMap.contains(firstKey)) {
|
if (variantMap.contains(firstKey)) {
|
||||||
if (dotIndex == -1) {
|
if (dotIndex == -1) {
|
||||||
return &variantMap[firstKey];
|
return &variantMap[firstKey];
|
||||||
|
@ -171,6 +171,6 @@ const QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath)
|
||||||
return valueForKeyPath(*static_cast<QVariantMap*>(variantMap[firstKey].data()), keyPath.mid(dotIndex + 1));
|
return valueForKeyPath(*static_cast<QVariantMap*>(variantMap[firstKey].data()), keyPath.mid(dotIndex + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,26 +17,26 @@
|
||||||
class HifiConfigVariantMap {
|
class HifiConfigVariantMap {
|
||||||
public:
|
public:
|
||||||
static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList);
|
static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList);
|
||||||
|
|
||||||
HifiConfigVariantMap();
|
HifiConfigVariantMap();
|
||||||
void loadMasterAndUserConfig(const QStringList& argumentList);
|
void loadMasterAndUserConfig(const QStringList& argumentList);
|
||||||
|
|
||||||
const QVariantMap& getMasterConfig() const { return _masterConfig; }
|
const QVariantMap& getMasterConfig() const { return _masterConfig; }
|
||||||
QVariantMap& getUserConfig() { return _userConfig; }
|
QVariantMap& getUserConfig() { return _userConfig; }
|
||||||
QVariantMap& getMergedConfig() { return _mergedConfig; }
|
QVariantMap& getMergedConfig() { return _mergedConfig; }
|
||||||
|
|
||||||
const QString& getUserConfigFilename() const { return _userConfigFilename; }
|
const QString& getUserConfigFilename() const { return _userConfigFilename; }
|
||||||
private:
|
private:
|
||||||
QString _userConfigFilename;
|
QString _userConfigFilename;
|
||||||
|
|
||||||
QVariantMap _masterConfig;
|
QVariantMap _masterConfig;
|
||||||
QVariantMap _userConfig;
|
QVariantMap _userConfig;
|
||||||
QVariantMap _mergedConfig;
|
QVariantMap _mergedConfig;
|
||||||
|
|
||||||
void loadMapFromJSONFile(QVariantMap& existingMap, const QString& filename);
|
void loadMapFromJSONFile(QVariantMap& existingMap, const QString& filename);
|
||||||
void addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap);
|
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
|
#endif // hifi_HifiConfigVariantMap_h
|
||||||
|
|
|
@ -51,15 +51,16 @@ void InfoView::show(const QString& path, bool firstOrChangedOnly) {
|
||||||
}
|
}
|
||||||
if (firstOrChangedOnly) {
|
if (firstOrChangedOnly) {
|
||||||
const QString lastVersion = infoVersion.get();
|
const QString lastVersion = infoVersion.get();
|
||||||
|
const QString version = fetchVersion(url);
|
||||||
// If we have version information stored
|
// If we have version information stored
|
||||||
if (lastVersion != QString::null) {
|
if (lastVersion != QString::null) {
|
||||||
// Check to see the document version. If it's valid and matches
|
// Check to see the document version. If it's valid and matches
|
||||||
// the stored version, we're done, so exit
|
// the stored version, we're done, so exit
|
||||||
const QString version = fetchVersion(url);
|
|
||||||
if (version == QString::null || version == lastVersion) {
|
if (version == QString::null || version == lastVersion) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
infoVersion.set(version);
|
||||||
}
|
}
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
QString infoViewName(NAME + "_" + path);
|
QString infoViewName(NAME + "_" + path);
|
||||||
|
|
Loading…
Reference in a new issue