Merge branch 'master' of github.com:highfidelity/hifi into atp-client

This commit is contained in:
Seth Alves 2017-07-06 17:28:55 -07:00
commit b8875a8902
21 changed files with 490 additions and 152 deletions

View file

@ -17,8 +17,8 @@
#include <QtCore/QThread>
#include <LogHandler.h>
#include <SharedUtil.h>
#include <HifiConfigVariantMap.h>
#include <SharedUtil.h>
#include <ShutdownEventListener.h>
#include "Assignment.h"

View file

@ -50,6 +50,7 @@
{
"label": "Places / Paths",
"html_id": "places_paths",
"restart": false,
"settings": [
{
"name": "paths",
@ -75,6 +76,7 @@
{
"name": "descriptors",
"label": "Description",
"restart": false,
"help": "This data will be queryable from your server. It may be collected by High Fidelity and used to share your domain with others.",
"settings": [
{

View file

@ -2,11 +2,11 @@ $(document).ready(function(){
// setup the underscore templates
var nodeTemplate = _.template($('#nodes-template').html());
var queuedTemplate = _.template($('#queued-template').html());
// setup a function to grab the assignments
function getNodesAndAssignments() {
$.getJSON("nodes.json", function(json){
json.nodes.sort(function(a, b){
if (a.type === b.type) {
if (a.uptime < b.uptime) {
@ -16,36 +16,50 @@ $(document).ready(function(){
} else {
return 0;
}
}
}
if (a.type === "agent" && b.type !== "agent") {
return 1;
} else if (b.type === "agent" && a.type !== "agent") {
return -1;
}
if (a.type > b.type) {
return 1;
}
if (a.type < b.type) {
return -1;
}
}
});
$('#nodes-table tbody').html(nodeTemplate(json));
}).fail(function(jqXHR, textStatus, errorThrown) {
// we assume a 401 means the DS has restarted
// and no longer has our OAuth produced uuid
// so just reload and re-auth
if (jqXHR.status == 401) {
location.reload();
}
});
$.getJSON("assignments.json", function(json){
$.getJSON("assignments.json", function(json){
$('#assignments-table tbody').html(queuedTemplate(json));
}).fail(function(jqXHR, textStatus, errorThrown) {
// we assume a 401 means the DS has restarted
// and no longer has our OAuth produced uuid
// so just reload and re-auth
if (jqXHR.status == 401) {
location.reload();
}
});
}
// do the first GET on page load
getNodesAndAssignments();
// grab the new assignments JSON every two seconds
var getNodesAndAssignmentsInterval = setInterval(getNodesAndAssignments, 2000);
// hook the node delete to the X button
$(document.body).on('click', '.glyphicon-remove', function(){
// fire off a delete for this node
@ -57,10 +71,10 @@ $(document).ready(function(){
}
});
});
$(document.body).on('click', '#kill-all-btn', function() {
var confirmed_kill = confirm("Are you sure?");
if (confirmed_kill == true) {
$.ajax({
url: "/nodes/",

View file

@ -40,11 +40,11 @@
#include <LogHandler.h>
#include <PathUtils.h>
#include <NumericalConstants.h>
#include <Trace.h>
#include <StatTracker.h>
#include "DomainServerNodeData.h"
#include "NodeConnectionData.h"
#include <Trace.h>
#include <StatTracker.h>
int const DomainServer::EXIT_CODE_REBOOT = 234923;
@ -162,8 +162,10 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_gatekeeper.preloadAllowedUserPublicKeys(); // so they can connect on first request
//send signal to DomainMetadata when descriptors changed
_metadata = new DomainMetadata(this);
connect(&_settingsManager, &DomainServerSettingsManager::settingsUpdated,
_metadata, &DomainMetadata::descriptorsChanged);
qDebug() << "domain-server is running";
static const QString AC_SUBNET_WHITELIST_SETTING_PATH = "security.ac_subnet_whitelist";
@ -1972,7 +1974,8 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
return _settingsManager.handleAuthenticatedHTTPRequest(connection, url);
}
const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID";
static const QString HIFI_SESSION_COOKIE_KEY = "DS_WEB_SESSION_UUID";
static const QString STATE_QUERY_KEY = "state";
bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &url, bool skipSubHandler) {
qDebug() << "HTTPS request received at" << url.toString();
@ -1983,10 +1986,9 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
const QString CODE_QUERY_KEY = "code";
QString authorizationCode = codeURLQuery.queryItemValue(CODE_QUERY_KEY);
const QString STATE_QUERY_KEY = "state";
QUuid stateUUID = QUuid(codeURLQuery.queryItemValue(STATE_QUERY_KEY));
if (!authorizationCode.isEmpty() && !stateUUID.isNull()) {
if (!authorizationCode.isEmpty() && !stateUUID.isNull() && _webAuthenticationStateSet.remove(stateUUID)) {
// fire off a request with this code and state to get an access token for the user
const QString OAUTH_TOKEN_REQUEST_PATH = "/oauth/token";
@ -2004,47 +2006,83 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply* tokenReply = NetworkAccessManager::getInstance().post(tokenRequest, tokenPostBody.toLocal8Bit());
connect(tokenReply, &QNetworkReply::finished, this, &DomainServer::tokenGrantFinished);
if (_webAuthenticationStateSet.remove(stateUUID)) {
// this is a web user who wants to auth to access web interface
// we hold the response back to them until we get their profile information
// and can decide if they are let in or not
// add this connection to our list of pending connections so that we can hold the response
_pendingOAuthConnections.insert(stateUUID, connection);
QEventLoop loop;
connect(tokenReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
// set the state UUID on the reply so that we can associate the response with the connection later
tokenReply->setProperty(STATE_QUERY_KEY.toLocal8Bit(), stateUUID);
// start the loop for the token request
loop.exec();
return true;
} else {
connection->respond(HTTPConnection::StatusCode400);
QNetworkReply* profileReply = profileRequestGivenTokenReply(tokenReply);
return true;
}
} else {
return false;
}
}
// stop the loop once the profileReply is complete
connect(profileReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
HTTPSConnection* DomainServer::connectionFromReplyWithState(QNetworkReply* reply) {
// grab the UUID state property from the reply
QUuid stateUUID = reply->property(STATE_QUERY_KEY.toLocal8Bit()).toUuid();
// restart the loop for the profile request
loop.exec();
if (!stateUUID.isNull()) {
return _pendingOAuthConnections.take(stateUUID);
} else {
return nullptr;
}
}
void DomainServer::tokenGrantFinished() {
auto tokenReply = qobject_cast<QNetworkReply*>(sender());
if (tokenReply) {
if (tokenReply->error() == QNetworkReply::NoError) {
// now that we have a token for this profile, send off a profile request
QNetworkReply* profileReply = profileRequestGivenTokenReply(tokenReply);
// forward along the state UUID that we kept with the token request
profileReply->setProperty(STATE_QUERY_KEY.toLocal8Bit(), tokenReply->property(STATE_QUERY_KEY.toLocal8Bit()));
connect(profileReply, &QNetworkReply::finished, this, &DomainServer::profileRequestFinished);
} else {
// the token grant failed, send back a 500 (assuming the connection is still around)
auto connection = connectionFromReplyWithState(tokenReply);
if (connection) {
connection->respond(HTTPConnection::StatusCode500);
}
}
tokenReply->deleteLater();
}
}
void DomainServer::profileRequestFinished() {
auto profileReply = qobject_cast<QNetworkReply*>(sender());
if (profileReply) {
auto connection = connectionFromReplyWithState(profileReply);
if (connection) {
if (profileReply->error() == QNetworkReply::NoError) {
// call helper method to get cookieHeaders
Headers cookieHeaders = setupCookieHeadersFromProfileReply(profileReply);
connection->respond(HTTPConnection::StatusCode302, QByteArray(),
HTTPConnection::DefaultContentType, cookieHeaders);
delete tokenReply;
delete profileReply;
// we've redirected the user back to our homepage
return true;
} else {
// the profile request failed, send back a 500 (assuming the connection is still around)
connection->respond(HTTPConnection::StatusCode500);
}
}
// respond with a 200 code indicating that login is complete
connection->respond(HTTPConnection::StatusCode200);
return true;
} else {
return false;
profileReply->deleteLater();
}
}
@ -2104,22 +2142,31 @@ bool DomainServer::isAuthenticatedRequest(HTTPConnection* connection, const QUrl
// the user does not have allowed username or role, return 401
return false;
} else {
// re-direct this user to OAuth page
static const QByteArray REQUESTED_WITH_HEADER = "X-Requested-With";
static const QString XML_REQUESTED_WITH = "XMLHttpRequest";
// generate a random state UUID to use
QUuid stateUUID = QUuid::createUuid();
if (connection->requestHeaders().value(REQUESTED_WITH_HEADER) == XML_REQUESTED_WITH) {
// unauthorized XHR requests get a 401 and not a 302, since there isn't an XHR
// path to OAuth authorize
connection->respond(HTTPConnection::StatusCode401, UNAUTHENTICATED_BODY);
} else {
// re-direct this user to OAuth page
// add it to the set so we can handle the callback from the OAuth provider
_webAuthenticationStateSet.insert(stateUUID);
// generate a random state UUID to use
QUuid stateUUID = QUuid::createUuid();
QUrl authURL = oauthAuthorizationURL(stateUUID);
// add it to the set so we can handle the callback from the OAuth provider
_webAuthenticationStateSet.insert(stateUUID);
Headers redirectHeaders;
QUrl authURL = oauthAuthorizationURL(stateUUID);
redirectHeaders.insert("Location", authURL.toEncoded());
Headers redirectHeaders;
connection->respond(HTTPConnection::StatusCode302,
QByteArray(), HTTPConnection::DefaultContentType, redirectHeaders);
redirectHeaders.insert("Location", authURL.toEncoded());
connection->respond(HTTPConnection::StatusCode302,
QByteArray(), HTTPConnection::DefaultContentType, redirectHeaders);
}
// we don't know about this user yet, so they are not yet authenticated
return false;

View file

@ -111,6 +111,9 @@ private slots:
void updateDownstreamNodes();
void updateUpstreamNodes();
void tokenGrantFinished();
void profileRequestFinished();
signals:
void iceServerChanged();
void userConnected();
@ -178,6 +181,8 @@ private:
void updateReplicationNodes(ReplicationServerDirection direction);
HTTPSConnection* connectionFromReplyWithState(QNetworkReply* reply);
SubnetList _acSubnetWhitelist;
std::vector<QString> _replicatedUsernames;
@ -235,6 +240,8 @@ private:
bool _sendICEServerAddressToMetaverseAPIInProgress { false };
bool _sendICEServerAddressToMetaverseAPIRedo { false };
QHash<QUuid, QPointer<HTTPSConnection>> _pendingOAuthConnections;
};

View file

@ -1198,6 +1198,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
static const QString SECURITY_ROOT_KEY = "security";
static const QString AC_SUBNET_WHITELIST_KEY = "ac_subnet_whitelist";
static const QString BROADCASTING_KEY = "broadcasting";
static const QString DESCRIPTION_ROOT_KEY = "descriptors";
auto& settingsVariant = _configMap.getConfig();
bool needRestart = false;
@ -1249,7 +1250,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
if (!matchingDescriptionObject.isEmpty()) {
updateSetting(rootKey, rootValue, *thisMap, matchingDescriptionObject);
if (rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY) {
if (rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY && rootKey != SETTINGS_PATHS_KEY ) {
needRestart = true;
}
} else {
@ -1265,7 +1266,7 @@ bool DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJ
if (!matchingDescriptionObject.isEmpty()) {
const QJsonValue& settingValue = rootValue.toObject()[settingKey];
updateSetting(settingKey, settingValue, *thisMap, matchingDescriptionObject);
if ((rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY)
if ((rootKey != SECURITY_ROOT_KEY && rootKey != BROADCASTING_KEY && rootKey != DESCRIPTION_ROOT_KEY)
|| settingKey == AC_SUBNET_WHITELIST_KEY) {
needRestart = true;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View file

@ -0,0 +1,64 @@
//
// ImageMessageBox.qml
//
// Created by Dante Ruiz on 7/5/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "../styles-uit"
Item {
id: imageBox
visible: false
anchors.fill: parent
property alias source: image.source
property alias imageWidth: image.width
property alias imageHeight: image.height
Rectangle {
anchors.fill: parent
color: "black"
opacity: 0.3
}
Image {
id: image
anchors.centerIn: parent
HiFiGlyphs {
id: closeGlyphButton
text: hifi.glyphs.close
size: 25
anchors {
top: parent.top
topMargin: 15
right: parent.right
rightMargin: 15
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
parent.text = hifi.glyphs.closeInverted;
}
onExited: {
parent.text = hifi.glyphs.close;
}
onClicked: {
imageBox.visible = false;
}
}
}
}
}

View file

@ -65,7 +65,7 @@ Rectangle {
HiFiGlyphs {
id: image
text: hifi.glyphs.avatar1
text: hifi.glyphs.avatarTPose
size: 190
color: hifi.colors.white

View file

@ -16,6 +16,7 @@ import "../../controls-uit" as HifiControls
StackView {
id: stack
initialItem: inputConfiguration
property alias messageVisible: imageMessageBox.visible
Rectangle {
id: inputConfiguration
anchors.fill: parent
@ -26,6 +27,15 @@ StackView {
property var pluginSettings: null
HifiControls.ImageMessageBox {
id: imageMessageBox
anchors.fill: parent
z: 2000
imageWidth: 442
imageHeight: 670
source: "../../../images/calibration-help.png"
}
Rectangle {
width: inputConfiguration.width
height: 1
@ -167,7 +177,7 @@ StackView {
loader.item.pluginName = box.currentText;
}
}
if (loader.item.hasOwnProperty("displayInformation")) {
loader.item.displayConfiguration();
}
@ -183,20 +193,20 @@ StackView {
return InputConfiguration.activeInputPlugins();
}
}
function initialize() {
changeSource();
}
function changeSource() {
loader.source = "";
var source = "";
if (box.currentText == "Vive") {
source = InputConfiguration.configurationLayout("OpenVR");
} else {
} else {
source = InputConfiguration.configurationLayout(box.currentText);
}
loader.source = source;
if (source === "") {
box.label = "(not configurable)";
@ -204,14 +214,14 @@ StackView {
box.label = "";
}
}
Timer {
id: timer
repeat: false
interval: 300
onTriggered: initialize()
}
Component.onCompleted: {
timer.start();
}

View file

@ -50,9 +50,12 @@ Rectangle {
readonly property int apply: 1
readonly property int applyAndCalibrate: 2
readonly property int calibrate: 3
}
MouseArea {
id: mouseArea
@ -64,6 +67,7 @@ Rectangle {
mouse.accepted = false;
}
}
color: hifi.colors.baseGray
RalewayBold {
@ -146,6 +150,7 @@ Rectangle {
label: "Y: offset"
minimumValue: -10
stepSize: 0.0254
value: -0.05
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
@ -161,15 +166,16 @@ Rectangle {
minimumValue: -10
stepSize: 0.0254
decimals: 4
value: -0.05
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
sendConfigurationSettings();
}
}
}
RalewayBold {
id: hands
@ -245,7 +251,7 @@ Rectangle {
anchors.left: openVrConfiguration.left
anchors.leftMargin: leftMargin + 10
spacing: 10
HifiControls.SpinBox {
id: handYOffset
decimals: 4
@ -269,7 +275,7 @@ Rectangle {
stepSize: 0.0254
decimals: 4
colorScheme: hifi.colorSchemes.dark
onEditingFinished: {
sendConfigurationSettings();
}
@ -290,6 +296,52 @@ Rectangle {
anchors.leftMargin: leftMargin
}
RalewayRegular {
id: info
text: "See Recommended Tracker Placement"
color: hifi.colors.blueHighlight
size: 10
anchors {
left: additional.right
leftMargin: 10
verticalCenter: additional.verticalCenter
}
Rectangle {
id: selected
color: hifi.colors.blueHighlight
width: info.width
height: 1
anchors {
top: info.bottom
topMargin: 1
left: info.left
right: info.right
}
visible: false
}
MouseArea {
anchors.fill: parent;
hoverEnabled: true
onEntered: {
selected.visible = true;
}
onExited: {
selected.visible = false;
}
onClicked: {
stack.messageVisible = true;
}
}
}
Row {
id: feetConfig
anchors.top: additional.bottom
@ -379,6 +431,7 @@ Rectangle {
if (checked) {
hipBox.checked = true;
feetBox.checked = true;
shoulderBox.checked = false;
}
sendConfigurationSettings();
}
@ -416,6 +469,7 @@ Rectangle {
if (checked) {
hipBox.checked = true;
feetBox.checked = true;
chestBox.checked = false;
}
sendConfigurationSettings();
}
@ -463,7 +517,7 @@ Rectangle {
anchors.leftMargin: leftMargin
radius: hifi.buttons.radius
gradient: Gradient {
GradientStop {
position: 0.2
@ -479,7 +533,7 @@ Rectangle {
}
}
}
GradientStop {
position: 1.0
color: {
@ -495,10 +549,10 @@ Rectangle {
}
}
}
HiFiGlyphs {
id: glyphButton
color: enabled ? hifi.buttons.textColor[calibrationButton.color]
@ -512,7 +566,7 @@ Rectangle {
bottomMargin: 1
}
}
RalewayBold {
id: calibrationText
font.capitalization: Font.AllUppercase
@ -527,7 +581,7 @@ Rectangle {
topMargin: 7
}
}
MouseArea {
anchors.fill: parent
@ -549,19 +603,19 @@ Rectangle {
}
}
}
onPressed: {
calibrationButton.pressed = true;
}
onReleased: {
calibrationButton.pressed = false;
}
onEntered: {
calibrationButton.hovered = true;
}
onExited: {
calibrationButton.hovered = false;
}
@ -652,7 +706,7 @@ Rectangle {
RalewayBold {
id: advanceSettings
text: "Advance Settings"
text: "Advanced Settings"
size: 12
color: hifi.colors.white
@ -683,7 +737,7 @@ Rectangle {
RalewayBold {
id: viveDesktopText
size: 10
text: "Use vive devices in desktop mode"
text: "Use Vive devices in desktop mode"
color: hifi.colors.white
anchors {
@ -718,14 +772,14 @@ Rectangle {
calibratingScreen = screen.createObject();
stack.push(calibratingScreen);
}
if (status["calibrated"]) {
calibrationScreen.success();
if (status["UI"]) {
logAction("mocap_ui_success", status);
}
} else if (!status["calibrated"]) {
calibrationScreen.failure();
@ -840,11 +894,11 @@ Rectangle {
var handOverride = handSetting["override"];
var settingsChanged = false;
if (lastConfiguration["bodyConfiguration"] !== bodySetting) {
settingsChanged = true;
}
var lastHead = lastConfiguration["headConfiguration"];
if (lastHead["override"] !== headOverride) {
settingsChanged = true;
@ -854,13 +908,13 @@ Rectangle {
if (lastHand["override"] !== handOverride) {
settingsChanged = true;
}
if (settingsChanged) {
if ((!handOverride) && (!headOverride) && (bodySetting === "None")) {
state = buttonState.apply;
} else {
state = buttonState.applyAndCalibrate;
}
}
} else {
if (state == buttonState.apply) {
state = buttonState.disabled;
@ -868,7 +922,7 @@ Rectangle {
state = buttonState.calibrate;
}
}
lastConfiguration = settings;
}
@ -885,7 +939,7 @@ Rectangle {
state = buttonState.disabled;
} else {
state = buttonState.calibrate;
}
}
}
function updateCalibrationButton() {
@ -951,7 +1005,7 @@ Rectangle {
"Y": handYOffset.value,
"Z": handZOffset.value
}
var settingsObject = {
"bodyConfiguration": trackerConfiguration,
"headConfiguration": headObject,

View file

@ -94,10 +94,20 @@ StackView {
property bool keyboardEnabled: false
property bool keyboardRaised: false
property bool punctuationMode: false
width: parent.width
height: parent.height
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onPressed: {
parent.forceActiveFocus();
addressBarDialog.keyboardEnabled = false;
mouse.accepted = false;
}
}
anchors {
right: parent.right
left: parent.left
@ -227,9 +237,9 @@ StackView {
MouseArea {
anchors.fill: parent;
onClicked: {
if (!addressLine.focus || !HMD.active) {
addressLine.focus = true;
addressLine.forceActiveFocus();
addressLine.focus = true;
addressLine.forceActiveFocus();
if (HMD.active) {
addressBarDialog.keyboardEnabled = HMD.active;
}
tabletRoot.playButtonClickSound();

View file

@ -336,5 +336,6 @@ Item {
readonly property string source: "\ue01c"
readonly property string playback_play: "\ue01d"
readonly property string stop_square: "\ue01e"
readonly property string avatarTPose: "\ue01f"
}
}

View file

@ -952,58 +952,68 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Make sure we don't time out during slow operations at startup
updateHeartbeat();
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
static const QString TESTER = "HIFI_TESTER";
auto gpuIdent = GPUIdent::getInstance();
auto glContextData = getGLContextData();
QJsonObject properties = {
{ "version", applicationVersion() },
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) },
{ "previousSessionCrashed", _previousSessionCrashed },
{ "previousSessionRuntime", sessionRunTime.get() },
{ "cpu_architecture", QSysInfo::currentCpuArchitecture() },
{ "kernel_type", QSysInfo::kernelType() },
{ "kernel_version", QSysInfo::kernelVersion() },
{ "os_type", QSysInfo::productType() },
{ "os_version", QSysInfo::productVersion() },
{ "gpu_name", gpuIdent->getName() },
{ "gpu_driver", gpuIdent->getDriver() },
{ "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) },
{ "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) },
{ "gl_version", glContextData["version"] },
{ "gl_vender", glContextData["vendor"] },
{ "gl_sl_version", glContextData["sl_version"] },
{ "gl_renderer", glContextData["renderer"] },
{ "ideal_thread_count", QThread::idealThreadCount() }
};
auto macVersion = QSysInfo::macVersion();
if (macVersion != QSysInfo::MV_None) {
properties["os_osx_version"] = QSysInfo::macVersion();
}
auto windowsVersion = QSysInfo::windowsVersion();
if (windowsVersion != QSysInfo::WV_None) {
properties["os_win_version"] = QSysInfo::windowsVersion();
}
ProcessorInfo procInfo;
if (getProcessorInfo(procInfo)) {
properties["processor_core_count"] = procInfo.numProcessorCores;
properties["logical_processor_count"] = procInfo.numLogicalProcessors;
properties["processor_l1_cache_count"] = procInfo.numProcessorCachesL1;
properties["processor_l2_cache_count"] = procInfo.numProcessorCachesL2;
properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3;
}
// add firstRun flag from settings to launch event
Setting::Handle<bool> firstRun { Settings::firstRun, true };
properties["first_run"] = firstRun.get();
// add the user's machine ID to the launch event
properties["machine_fingerprint"] = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
// once the settings have been loaded, check if we need to flip the default for UserActivityLogger
auto& userActivityLogger = UserActivityLogger::getInstance();
if (!userActivityLogger.isDisabledSettingSet()) {
// the user activity logger is opt-out for Interface
// but it's defaulted to disabled for other targets
// so we need to enable it here if it has never been disabled by the user
userActivityLogger.disable(false);
}
UserActivityLogger::getInstance().logAction("launch", properties);
if (userActivityLogger.isEnabled()) {
// sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value.
// The value will be 0 if the user blew away settings this session, which is both a feature and a bug.
static const QString TESTER = "HIFI_TESTER";
auto gpuIdent = GPUIdent::getInstance();
auto glContextData = getGLContextData();
QJsonObject properties = {
{ "version", applicationVersion() },
{ "tester", QProcessEnvironment::systemEnvironment().contains(TESTER) },
{ "previousSessionCrashed", _previousSessionCrashed },
{ "previousSessionRuntime", sessionRunTime.get() },
{ "cpu_architecture", QSysInfo::currentCpuArchitecture() },
{ "kernel_type", QSysInfo::kernelType() },
{ "kernel_version", QSysInfo::kernelVersion() },
{ "os_type", QSysInfo::productType() },
{ "os_version", QSysInfo::productVersion() },
{ "gpu_name", gpuIdent->getName() },
{ "gpu_driver", gpuIdent->getDriver() },
{ "gpu_memory", static_cast<qint64>(gpuIdent->getMemory()) },
{ "gl_version_int", glVersionToInteger(glContextData.value("version").toString()) },
{ "gl_version", glContextData["version"] },
{ "gl_vender", glContextData["vendor"] },
{ "gl_sl_version", glContextData["sl_version"] },
{ "gl_renderer", glContextData["renderer"] },
{ "ideal_thread_count", QThread::idealThreadCount() }
};
auto macVersion = QSysInfo::macVersion();
if (macVersion != QSysInfo::MV_None) {
properties["os_osx_version"] = QSysInfo::macVersion();
}
auto windowsVersion = QSysInfo::windowsVersion();
if (windowsVersion != QSysInfo::WV_None) {
properties["os_win_version"] = QSysInfo::windowsVersion();
}
ProcessorInfo procInfo;
if (getProcessorInfo(procInfo)) {
properties["processor_core_count"] = procInfo.numProcessorCores;
properties["logical_processor_count"] = procInfo.numLogicalProcessors;
properties["processor_l1_cache_count"] = procInfo.numProcessorCachesL1;
properties["processor_l2_cache_count"] = procInfo.numProcessorCachesL2;
properties["processor_l3_cache_count"] = procInfo.numProcessorCachesL3;
}
properties["first_run"] = firstRun.get();
// add the user's machine ID to the launch event
properties["machine_fingerprint"] = uuidStringWithoutCurlyBraces(FingerprintUtils::getMachineFingerprint());
userActivityLogger.logAction("launch", properties);
}
// Tell our entity edit sender about our known jurisdictions
_entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);

View file

@ -319,7 +319,7 @@ Menu::Menu() {
QString("../../hifi/tablet/TabletLodPreferences.qml"), "LodPreferencesDialog");
});
action = addActionToQMenuAndActionHash(settingsMenu, "Controller Settings");
action = addActionToQMenuAndActionHash(settingsMenu, "Controller Settings...");
connect(action, &QAction::triggered, [] {
auto tablet = DependencyManager::get<TabletScriptingInterface>()->getTablet("com.highfidelity.interface.tablet.system");
auto hmd = DependencyManager::get<HMDScriptingInterface>();

View file

@ -33,6 +33,7 @@ public:
public slots:
bool isEnabled() { return !_disabled.get(); }
bool isDisabledSettingSet() const { return _disabled.isSet(); }
void disable(bool disable);
void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters());
@ -53,7 +54,7 @@ private slots:
private:
UserActivityLogger();
Setting::Handle<bool> _disabled { "UserActivityLoggerDisabled", false };
Setting::Handle<bool> _disabled { "UserActivityLoggerDisabled", true };
QElapsedTimer _timer;
};

View file

@ -107,6 +107,7 @@ namespace Setting {
}
bool isSet() const {
maybeInit();
return _isSet;
}

View file

@ -0,0 +1,116 @@
"use strict";
//
// godView.js
// scripts/system/
//
// Created by Brad Hefta-Gaub on 1 Jun 2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
/* globals HMD, Script, Menu, Tablet, Camera */
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
(function() { // BEGIN LOCAL_SCOPE
var godView = false;
var GOD_CAMERA_OFFSET = -1; // 1 meter below the avatar
var GOD_VIEW_HEIGHT = 300; // 300 meter above the ground
var ABOVE_GROUND_DROP = 2;
var MOVE_BY = 1;
function moveTo(position) {
if (godView) {
MyAvatar.position = position;
Camera.position = Vec3.sum(MyAvatar.position, {x:0, y: GOD_CAMERA_OFFSET, z: 0});
} else {
MyAvatar.position = position;
}
}
function keyPressEvent(event) {
if (godView) {
switch(event.text) {
case "UP":
moveTo(Vec3.sum(MyAvatar.position, {x:0.0, y: 0, z: -1 * MOVE_BY}));
break;
case "DOWN":
moveTo(Vec3.sum(MyAvatar.position, {x:0, y: 0, z: MOVE_BY}));
break;
case "LEFT":
moveTo(Vec3.sum(MyAvatar.position, {x:-1 * MOVE_BY, y: 0, z: 0}));
break;
case "RIGHT":
moveTo(Vec3.sum(MyAvatar.position, {x:MOVE_BY, y: 0, z: 0}));
break;
}
}
}
function mousePress(event) {
if (godView) {
var pickRay = Camera.computePickRay(event.x, event.y);
var pointingAt = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction,300));
var moveToPosition = { x: pointingAt.x, y: MyAvatar.position.y, z: pointingAt.z };
moveTo(moveToPosition);
}
}
var oldCameraMode = Camera.mode;
function startGodView() {
if (!godView) {
oldCameraMode = Camera.mode;
MyAvatar.position = Vec3.sum(MyAvatar.position, {x:0, y: GOD_VIEW_HEIGHT, z: 0});
Camera.mode = "independent";
Camera.position = Vec3.sum(MyAvatar.position, {x:0, y: GOD_CAMERA_OFFSET, z: 0});
Camera.orientation = Quat.fromPitchYawRollDegrees(-90,0,0);
godView = true;
}
}
function endGodView() {
if (godView) {
Camera.mode = oldCameraMode;
MyAvatar.position = Vec3.sum(MyAvatar.position, {x:0, y: (-1 * GOD_VIEW_HEIGHT) + ABOVE_GROUND_DROP, z: 0});
godView = false;
}
}
var button;
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
function onClicked() {
if (godView) {
endGodView();
} else {
startGodView();
}
}
button = tablet.addButton({
icon: "icons/tablet-icons/switch-desk-i.svg", // FIXME - consider a better icon from Alan
text: "God View"
});
button.clicked.connect(onClicked);
Controller.keyPressEvent.connect(keyPressEvent);
Controller.mousePressEvent.connect(mousePress);
Script.scriptEnding.connect(function () {
if (godView) {
endGodView();
}
button.clicked.disconnect(onClicked);
if (tablet) {
tablet.removeButton(button);
}
Controller.keyPressEvent.disconnect(keyPressEvent);
Controller.mousePressEvent.disconnect(mousePress);
});
}()); // END LOCAL_SCOPE

View file

@ -482,10 +482,10 @@ function populateNearbyUserList(selectData, oldAudioData) {
isPresent: true,
isReplicated: avatar.isReplicated
};
// Everyone needs to see admin status. Username and fingerprint returns default constructor output if the requesting user isn't an admin.
Users.requestUsernameFromID(id);
if (id) {
addAvatarNode(id); // No overlay for ourselves
// Everyone needs to see admin status. Username and fingerprint returns default constructor output if the requesting user isn't an admin.
Users.requestUsernameFromID(id);
avatarsOfInterest[id] = true;
} else {
// Return our username from the Account API

View file

@ -46,7 +46,6 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT));
parser.addOption(listenPortOption);
if (!parser.parse(QCoreApplication::arguments())) {
qCritical() << parser.errorText() << endl;
parser.showHelp();
@ -70,6 +69,7 @@ ACClientApp::ACClientApp(int argc, char* argv[]) :
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtInfoMsg, false);
const_cast<QLoggingCategory*>(&shared())->setEnabled(QtWarningMsg, false);
}
QString domainServerAddress = "127.0.0.1:40103";
if (parser.isSet(domainAddressOption)) {