mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 16:55:07 +02:00
Merge branch 'master' into fix/domain-threading-defaults
This commit is contained in:
commit
ab685838b0
118 changed files with 17738 additions and 222 deletions
|
@ -1,6 +1,6 @@
|
|||
# Build Linux
|
||||
|
||||
*Last Updated on January 20, 2020*
|
||||
*Last Updated on April 11, 2020*
|
||||
|
||||
Please read the [general build guide](BUILD.md) for information on dependencies required for all platforms. Only Linux specific instructions are found in this file.
|
||||
|
||||
|
|
|
@ -615,6 +615,10 @@ void Agent::setIsAvatar(bool isAvatar) {
|
|||
delete _avatarQueryTimer;
|
||||
_avatarQueryTimer = nullptr;
|
||||
|
||||
// Clear the skeleton model so that if agent is set to an avatar again the skeleton model is (re)loaded.
|
||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||
scriptedAvatar->setSkeletonModelURL(QUrl());
|
||||
|
||||
// The avatar mixer never times out a connection (e.g., based on identity or data packets)
|
||||
// but rather keeps avatars in its list as long as "connected". As a result, clients timeout
|
||||
// when we stop sending identity, but then get woken up again by the mixer itself, which sends
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <AvatarLogging.h>
|
||||
#include <EntityItem.h>
|
||||
#include <EntityItemProperties.h>
|
||||
#include <NetworkingConstants.h>
|
||||
|
||||
|
||||
ScriptableAvatar::ScriptableAvatar() {
|
||||
|
@ -221,7 +222,7 @@ void ScriptableAvatar::updateJointMappings() {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(_skeletonModelURL);
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
DependencyManager::get<ResourceRequestObserver>()->update(
|
||||
_skeletonModelURL, -1, "AvatarData::updateJointMappings");
|
||||
QNetworkReply* networkReply = networkAccessManager.get(networkRequest);
|
||||
|
|
|
@ -871,7 +871,7 @@ Function PostInstallOptionsPage
|
|||
Pop $LaunchConsoleNowCheckbox
|
||||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetInstallOption $LaunchConsoleNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_CHECKED}
|
||||
!insertmacro SetInstallOption $LaunchConsoleNowCheckbox @SERVER_LAUNCH_NOW_REG_KEY@ ${BST_UNCHECKED}
|
||||
${StrContains} $substringResult "/forceNoLaunchServer" $CMDLINE
|
||||
${IfNot} $substringResult == ""
|
||||
${NSD_SetState} $LaunchConsoleNowCheckbox ${BST_UNCHECKED}
|
||||
|
@ -887,7 +887,7 @@ Function PostInstallOptionsPage
|
|||
IntOp $CurrentOffset $CurrentOffset + 15
|
||||
|
||||
; set the checkbox state depending on what is present in the registry
|
||||
!insertmacro SetInstallOption $ConsoleStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_CHECKED}
|
||||
!insertmacro SetInstallOption $ConsoleStartupCheckbox @CONSOLE_STARTUP_REG_KEY@ ${BST_UNCHECKED}
|
||||
|
||||
${NSD_CreateCheckbox} 0 $CurrentOffset$OffsetUnits 100% 10u "&Perform a clean install (Delete older settings and content)"
|
||||
Pop $CleanInstallCheckbox
|
||||
|
@ -940,8 +940,8 @@ Function ReadInstallTypes
|
|||
StrCpy $Express "1"
|
||||
|
||||
StrCpy $DesktopClientState ${BST_CHECKED}
|
||||
StrCpy $ConsoleStartupState ${BST_CHECKED}
|
||||
StrCpy $LaunchConsoleNowState ${BST_CHECKED}
|
||||
StrCpy $ConsoleStartupState ${BST_UNCHECKED}
|
||||
StrCpy $LaunchConsoleNowState ${BST_UNCHECKED}
|
||||
StrCpy $LaunchClientNowState ${BST_CHECKED}
|
||||
StrCpy $CleanInstallState ${BST_UNCHECKED}
|
||||
StrCpy $DesktopConsoleState ${BST_UNCHECKED}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"settings": [
|
||||
{
|
||||
"name": "metaverse",
|
||||
"label": "Metaverse / Networking",
|
||||
"label": "Networking / Metaverse",
|
||||
"settings": [
|
||||
{
|
||||
"name": "access_token",
|
||||
|
@ -57,6 +57,39 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "authentication",
|
||||
"label": "Networking / WordPress OAuth2",
|
||||
"settings": [
|
||||
{
|
||||
"name": "enable_oauth2",
|
||||
"label": "Enable OAuth2 Authentication",
|
||||
"help": "Allow a WordPress-based (miniOrange) OAuth2 service to assign users to groups based on their role with the service.",
|
||||
"default": false,
|
||||
"type": "checkbox",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "oauth2_url_path",
|
||||
"label": "Authentication URL",
|
||||
"help": "The URL that the Interface will use to login via OAuth2.",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "wordpress_url_base",
|
||||
"label": "WordPress API URL Base",
|
||||
"help": "The URL base that the domain server will use to make WordPress API calls. Typically \"https://oursite.com/wp-json/\". However, if using non-pretty permalinks or otherwise get a 404 error then try \"https://oursite.com/?rest_route=/\".",
|
||||
"advanced": true
|
||||
},
|
||||
{
|
||||
"name": "plugin_client_id",
|
||||
"label": "WordPress Plugin Client ID",
|
||||
"help": "This is the client ID from the WordPress plugin configuration.",
|
||||
"advanced": true,
|
||||
"backup": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Monitoring",
|
||||
"name": "monitoring",
|
||||
|
@ -381,6 +414,7 @@
|
|||
"name": "group_permissions",
|
||||
"type": "table",
|
||||
"caption": "Permissions for Users in Groups",
|
||||
"help": "For groups that are provided from WordPress you need to denote them by putting an \"@\" symbol in front of each item, e.g., \"@silver\".",
|
||||
"categorize_by_key": "permissions_id",
|
||||
"can_add_new_categories": true,
|
||||
"can_add_new_rows": false,
|
||||
|
@ -509,6 +543,7 @@
|
|||
"name": "group_forbiddens",
|
||||
"type": "table",
|
||||
"caption": "Permissions Denied to Users in Groups",
|
||||
"help": "For groups that are provided from WordPress you need to denote them by putting an \"@\" symbol in front of each item, e.g., \"@silver\".",
|
||||
"categorize_by_key": "permissions_id",
|
||||
"can_add_new_categories": true,
|
||||
"can_add_new_rows": false,
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 2015-08-24.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -94,6 +95,9 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
} else if (!STATICALLY_ASSIGNED_NODES.contains(nodeConnection.nodeType)) {
|
||||
QByteArray usernameSignature;
|
||||
|
||||
QString domainUsername;
|
||||
QStringList domainTokens;
|
||||
|
||||
if (message->getBytesLeftToRead() > 0) {
|
||||
// read username from packet
|
||||
packetStream >> username;
|
||||
|
@ -101,10 +105,25 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
if (message->getBytesLeftToRead() > 0) {
|
||||
// read user signature from packet
|
||||
packetStream >> usernameSignature;
|
||||
|
||||
if (message->getBytesLeftToRead() > 0) {
|
||||
// Read domain username from packet.
|
||||
packetStream >> domainUsername;
|
||||
domainUsername = domainUsername.toLower(); // Domain usernames are case-insensitive; internally lower-case.
|
||||
|
||||
if (message->getBytesLeftToRead() > 0) {
|
||||
// Read domain tokens from packet.
|
||||
|
||||
QString domainTokensString;
|
||||
packetStream >> domainTokensString;
|
||||
domainTokens = domainTokensString.split(":");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node = processAgentConnectRequest(nodeConnection, username, usernameSignature);
|
||||
node = processAgentConnectRequest(nodeConnection, username, usernameSignature,
|
||||
domainUsername, domainTokens.value(0), domainTokens.value(1));
|
||||
}
|
||||
|
||||
if (node) {
|
||||
|
@ -142,7 +161,8 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer<ReceivedMessag
|
|||
}
|
||||
}
|
||||
|
||||
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
|
||||
NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QString verifiedUsername,
|
||||
QString verifiedDomainUserName, const QHostAddress& senderAddress,
|
||||
const QString& hardwareAddress, const QUuid& machineFingerprint) {
|
||||
NodePermissions userPerms;
|
||||
|
||||
|
@ -155,6 +175,27 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
#endif
|
||||
}
|
||||
|
||||
// If this user is a known member of a domain group, give them the implied permissions.
|
||||
// Do before processing verifiedUsername in case user is logged into the metaverse and is a member of a blacklist group.
|
||||
if (!verifiedDomainUserName.isEmpty()) {
|
||||
auto userGroups = _domainGroupMemberships[verifiedDomainUserName];
|
||||
foreach (QString userGroup, userGroups) {
|
||||
// A domain group is signified by a leading special character, "@".
|
||||
// Multiple domain groups may be specified in one domain server setting as a comma- and/or space-separated lists of
|
||||
// domain group names. For example, "@silver @Gold, @platinum".
|
||||
auto domainGroups = _server->_settingsManager.getDomainServerGroupNames()
|
||||
.filter(QRegularExpression("^(.*[\\s,])?" + QRegularExpression::escape(userGroup) + "([\\s,].*)?$",
|
||||
QRegularExpression::CaseInsensitiveOption));
|
||||
foreach(QString domainGroup, domainGroups) {
|
||||
userPerms |= _server->_settingsManager.getPermissionsForGroup(domainGroup, QUuid()); // No rank for domain groups.
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: domain user " << verifiedDomainUserName << "is in group:" << domainGroup
|
||||
<< "so:" << userPerms;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (verifiedUsername.isEmpty()) {
|
||||
userPerms |= _server->_settingsManager.getStandardPermissionsForName(NodePermissions::standardNameAnonymous);
|
||||
#ifdef WANT_DEBUG
|
||||
|
@ -256,6 +297,27 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin
|
|||
userPerms.setVerifiedUserName(verifiedUsername);
|
||||
}
|
||||
|
||||
// If this user is a known member of an domain group that is blacklisted, remove the implied permissions.
|
||||
if (!verifiedDomainUserName.isEmpty()) {
|
||||
auto userGroups = _domainGroupMemberships[verifiedDomainUserName];
|
||||
foreach(QString userGroup, userGroups) {
|
||||
// A domain group is signified by a leading special character, "@".
|
||||
// Multiple domain groups may be specified in one domain server setting as a comma- and/or space-separated lists of
|
||||
// domain group names. For example, "@silver @Gold, @platinum".
|
||||
auto domainGroups = _server->_settingsManager.getDomainServerBlacklistGroupNames()
|
||||
.filter(QRegularExpression("^(.*[\\s,])?" + QRegularExpression::escape(userGroup) + "([\\s,].*)?$",
|
||||
QRegularExpression::CaseInsensitiveOption));
|
||||
foreach(QString domainGroup, domainGroups) {
|
||||
userPerms &= ~_server->_settingsManager.getForbiddensForGroup(domainGroup, QUuid());
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: domain user is in blacklist group:" << domainGroup << "so:" << userPerms;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
userPerms.setVerifiedDomainUserName(verifiedDomainUserName);
|
||||
}
|
||||
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "| user-permissions: final:" << userPerms;
|
||||
#endif
|
||||
|
@ -275,6 +337,7 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
// the id and the username in NodePermissions will often be the same, but id is set before
|
||||
// authentication and verifiedUsername is only set once they user's key has been confirmed.
|
||||
QString verifiedUsername = node->getPermissions().getVerifiedUserName();
|
||||
QString verifiedDomainUserName = node->getPermissions().getVerifiedDomainUserName();
|
||||
NodePermissions userPerms(NodePermissionsKey(verifiedUsername, 0));
|
||||
|
||||
if (node->getPermissions().isAssignment) {
|
||||
|
@ -309,7 +372,8 @@ void DomainGatekeeper::updateNodePermissions() {
|
|||
sendingAddress == QHostAddress::LocalHost);
|
||||
}
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, connectingAddr.getAddress(), hardwareAddress, machineFingerprint);
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, verifiedDomainUserName,
|
||||
connectingAddr.getAddress(), hardwareAddress, machineFingerprint);
|
||||
}
|
||||
|
||||
node->setPermissions(userPerms);
|
||||
|
@ -387,12 +451,19 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
|
|||
return newNode;
|
||||
}
|
||||
|
||||
const QString AUTHENTICATION_ENABLE_OAUTH2 = "authentication.enable_oauth2";
|
||||
const QString AUTHENTICATION_OAUTH2_URL_PATH = "authentication.oauth2_url_path";
|
||||
const QString AUTHENTICATION_WORDPRESS_URL_BASE = "authentication.wordpress_url_base";
|
||||
const QString AUTHENTICATION_PLUGIN_CLIENT_ID = "authentication.plugin_client_id";
|
||||
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
|
||||
const QString MAXIMUM_USER_CAPACITY_REDIRECT_LOCATION = "security.maximum_user_capacity_redirect_location";
|
||||
|
||||
SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
const QString& username,
|
||||
const QByteArray& usernameSignature) {
|
||||
const QByteArray& usernameSignature,
|
||||
const QString& domainUsername,
|
||||
const QString& domainAccessToken,
|
||||
const QString& domainRefreshToken) {
|
||||
|
||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||
|
||||
|
@ -419,7 +490,9 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
#ifdef WANT_DEBUG
|
||||
qDebug() << "stalling login because we have no username-signature:" << username;
|
||||
#endif
|
||||
return SharedNodePointer();
|
||||
if (!domainHasLogin() || domainUsername.isEmpty()) {
|
||||
return SharedNodePointer();
|
||||
}
|
||||
} else if (verifyUserSignature(username, usernameSignature, nodeConnection.senderSockAddr)) {
|
||||
// they sent us a username and the signature verifies it
|
||||
getGroupMemberships(username);
|
||||
|
@ -430,16 +503,70 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
|
|||
#ifdef WANT_DEBUG
|
||||
qDebug() << "stalling login because signature verification failed:" << username;
|
||||
#endif
|
||||
return SharedNodePointer();
|
||||
if (!domainHasLogin() || domainUsername.isEmpty()) {
|
||||
return SharedNodePointer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, nodeConnection.senderSockAddr.getAddress(),
|
||||
nodeConnection.hardwareAddress, nodeConnection.machineFingerprint);
|
||||
// The domain may have its own users and groups.
|
||||
QString verifiedDomainUsername;
|
||||
QStringList verifiedDomainUserGroups;
|
||||
if (domainHasLogin() && !domainUsername.isEmpty()) {
|
||||
|
||||
if (domainAccessToken.isEmpty()) {
|
||||
// User is attempting to prove their domain identity.
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "Stalling login because we have no domain OAuth2 tokens:" << domainUsername;
|
||||
#endif
|
||||
return SharedNodePointer();
|
||||
|
||||
} else if (needToVerifyDomainUserIdentity(domainUsername, domainAccessToken, domainRefreshToken)) {
|
||||
// User's domain identity needs to be confirmed.
|
||||
requestDomainUser(domainUsername, domainAccessToken, domainRefreshToken);
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "Stalling login because we haven't authenticated user yet:" << domainUsername;
|
||||
#endif
|
||||
|
||||
} else if (verifyDomainUserIdentity(domainUsername, domainAccessToken, domainRefreshToken,
|
||||
nodeConnection.senderSockAddr)) {
|
||||
// User's domain identity is confirmed.
|
||||
verifiedDomainUsername = domainUsername;
|
||||
|
||||
} else {
|
||||
// User's domain identity didn't check out.
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "Stalling login because domain user verification failed:" << domainUsername;
|
||||
#endif
|
||||
return SharedNodePointer();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, verifiedDomainUsername,
|
||||
nodeConnection.senderSockAddr.getAddress(), nodeConnection.hardwareAddress,
|
||||
nodeConnection.machineFingerprint);
|
||||
|
||||
if (!userPerms.can(NodePermissions::Permission::canConnectToDomain)) {
|
||||
sendConnectionDeniedPacket("You lack the required permissions to connect to this domain.",
|
||||
nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorized);
|
||||
if (domainHasLogin()) {
|
||||
QString domainAuthURL;
|
||||
auto domainAuthURLVariant = _server->_settingsManager.valueForKeyPath(AUTHENTICATION_OAUTH2_URL_PATH);
|
||||
if (domainAuthURLVariant.canConvert<QString>()) {
|
||||
domainAuthURL = domainAuthURLVariant.toString();
|
||||
}
|
||||
QString domainAuthClientID;
|
||||
auto domainAuthClientIDVariant = _server->_settingsManager.valueForKeyPath(AUTHENTICATION_PLUGIN_CLIENT_ID);
|
||||
if (domainAuthClientIDVariant.canConvert<QString>()) {
|
||||
domainAuthClientID = domainAuthClientIDVariant.toString();
|
||||
}
|
||||
|
||||
sendConnectionDeniedPacket("You lack the required domain permissions to connect to this domain.",
|
||||
nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedDomain,
|
||||
domainAuthURL + "|" + domainAuthClientID);
|
||||
} else {
|
||||
sendConnectionDeniedPacket("You lack the required metaverse permissions to connect to this domain.",
|
||||
nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedMetaverse);
|
||||
}
|
||||
#ifdef WANT_DEBUG
|
||||
qDebug() << "stalling login due to permissions:" << username;
|
||||
#endif
|
||||
|
@ -600,15 +727,15 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
|||
return true;
|
||||
|
||||
} else {
|
||||
// we only send back a LoginError if this wasn't an "optimistic" key
|
||||
// we only send back a LoginErrorMetaverse if this wasn't an "optimistic" key
|
||||
// (a key that we hoped would work but is probably stale)
|
||||
|
||||
if (!senderSockAddr.isNull() && !isOptimisticKey) {
|
||||
qDebug() << "Error decrypting username signature for" << username << "- denying connection.";
|
||||
qDebug() << "Error decrypting metaverse username signature for" << username << "- denying connection.";
|
||||
sendConnectionDeniedPacket("Error decrypting username signature.", senderSockAddr,
|
||||
DomainHandler::ConnectionRefusedReason::LoginError);
|
||||
DomainHandler::ConnectionRefusedReason::LoginErrorMetaverse);
|
||||
} else if (!senderSockAddr.isNull()) {
|
||||
qDebug() << "Error decrypting username signature for" << username << "with optimisitic key -"
|
||||
qDebug() << "Error decrypting metaverse username signature for" << username << "with optimistic key -"
|
||||
<< "re-requesting public key and delaying connection";
|
||||
}
|
||||
|
||||
|
@ -622,7 +749,7 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
|||
if (!senderSockAddr.isNull()) {
|
||||
qDebug() << "Couldn't convert data to RSA key for" << username << "- denying connection.";
|
||||
sendConnectionDeniedPacket("Couldn't convert data to RSA key.", senderSockAddr,
|
||||
DomainHandler::ConnectionRefusedReason::LoginError);
|
||||
DomainHandler::ConnectionRefusedReason::LoginErrorMetaverse);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -635,6 +762,25 @@ bool DomainGatekeeper::verifyUserSignature(const QString& username,
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool DomainGatekeeper::needToVerifyDomainUserIdentity(const QString& username, const QString& accessToken,
|
||||
const QString& refreshToken) {
|
||||
return !_verifiedDomainUserIdentities.contains(username)
|
||||
|| _verifiedDomainUserIdentities.value(username) != QPair<QString, QString>(accessToken, refreshToken);
|
||||
}
|
||||
|
||||
bool DomainGatekeeper::verifyDomainUserIdentity(const QString& username, const QString& accessToken,
|
||||
const QString& refreshToken, const HifiSockAddr& senderSockAddr) {
|
||||
if (_verifiedDomainUserIdentities.contains(username)
|
||||
&& _verifiedDomainUserIdentities.value(username) == QPair<QString, QString>(accessToken, refreshToken)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
sendConnectionDeniedPacket("Error verifying domain user.", senderSockAddr,
|
||||
DomainHandler::ConnectionRefusedReason::LoginErrorDomain);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DomainGatekeeper::isWithinMaxCapacity() {
|
||||
// find out what our maximum capacity is
|
||||
QVariant maximumUserCapacityVariant =
|
||||
|
@ -907,7 +1053,6 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) {
|
|||
AccountManagerAuth::Required,
|
||||
QNetworkAccessManager::PostOperation, callbackParams,
|
||||
QJsonDocument(json).toJson());
|
||||
|
||||
}
|
||||
|
||||
QString extractUsernameFromGroupMembershipsReply(QNetworkReply* requestReply) {
|
||||
|
@ -962,6 +1107,7 @@ void DomainGatekeeper::getIsGroupMemberErrorCallback(QNetworkReply* requestReply
|
|||
_inFlightGroupMembershipsRequests.remove(extractUsernameFromGroupMembershipsReply(requestReply));
|
||||
}
|
||||
|
||||
|
||||
void DomainGatekeeper::getDomainOwnerFriendsList() {
|
||||
JSONCallbackParameters callbackParams;
|
||||
callbackParams.callbackReceiver = this;
|
||||
|
@ -1010,6 +1156,7 @@ void DomainGatekeeper::getDomainOwnerFriendsListErrorCallback(QNetworkReply* req
|
|||
qDebug() << "getDomainOwnerFriendsList api call failed:" << requestReply->error();
|
||||
}
|
||||
|
||||
// ####### TODO: Domain equivalent or addition
|
||||
void DomainGatekeeper::refreshGroupsCache() {
|
||||
// if agents are connected to this domain, refresh our cached information about groups and memberships in such.
|
||||
getDomainOwnerFriendsList();
|
||||
|
@ -1029,7 +1176,7 @@ void DomainGatekeeper::refreshGroupsCache() {
|
|||
|
||||
updateNodePermissions();
|
||||
|
||||
#if WANT_DEBUG
|
||||
#ifdef WANT_DEBUG
|
||||
_server->_settingsManager.debugDumpGroupsState();
|
||||
#endif
|
||||
}
|
||||
|
@ -1061,3 +1208,91 @@ Node::LocalID DomainGatekeeper::findOrCreateLocalID(const QUuid& uuid) {
|
|||
_localIDs.insert(newLocalID);
|
||||
return newLocalID;
|
||||
}
|
||||
|
||||
|
||||
bool DomainGatekeeper::domainHasLogin() {
|
||||
// The domain may have its own users and groups in a WordPress site.
|
||||
return _server->_settingsManager.valueForKeyPath(AUTHENTICATION_ENABLE_OAUTH2).toBool()
|
||||
&& !_server->_settingsManager.valueForKeyPath(AUTHENTICATION_OAUTH2_URL_PATH).toString().isEmpty()
|
||||
&& !_server->_settingsManager.valueForKeyPath(AUTHENTICATION_WORDPRESS_URL_BASE).toString().isEmpty()
|
||||
&& !_server->_settingsManager.valueForKeyPath(AUTHENTICATION_PLUGIN_CLIENT_ID).toString().isEmpty();
|
||||
}
|
||||
|
||||
void DomainGatekeeper::requestDomainUser(const QString& username, const QString& accessToken, const QString& refreshToken) {
|
||||
|
||||
if (_inFlightDomainUserIdentityRequests.contains(username)) {
|
||||
// Domain identify request for this username is already in progress.
|
||||
return;
|
||||
}
|
||||
_inFlightDomainUserIdentityRequests.insert(username, QPair<QString, QString>(accessToken, refreshToken));
|
||||
|
||||
if (_verifiedDomainUserIdentities.contains(username)) {
|
||||
_verifiedDomainUserIdentities.remove(username);
|
||||
}
|
||||
|
||||
QString apiBase = _server->_settingsManager.valueForKeyPath(AUTHENTICATION_WORDPRESS_URL_BASE).toString();
|
||||
if (!apiBase.endsWith("/")) {
|
||||
apiBase += "/";
|
||||
}
|
||||
|
||||
// Get data pertaining to "me", the user who generated the access token.
|
||||
const QString WORDPRESS_USER_ROUTE = "wp/v2/users/me";
|
||||
const QString WORDPRESS_USER_QUERY = "_fields=username,roles";
|
||||
QUrl domainUserURL = apiBase + WORDPRESS_USER_ROUTE + (apiBase.contains("?") ? "&" : "?") + WORDPRESS_USER_QUERY;
|
||||
|
||||
QNetworkRequest request;
|
||||
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
request.setRawHeader(QByteArray("Authorization"), QString("Bearer " + accessToken).toUtf8());
|
||||
|
||||
QByteArray formData; // No data to send.
|
||||
|
||||
request.setUrl(domainUserURL);
|
||||
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkReply* requestReply = networkAccessManager.post(request, formData);
|
||||
connect(requestReply, &QNetworkReply::finished, this, &DomainGatekeeper::requestDomainUserFinished);
|
||||
}
|
||||
|
||||
void DomainGatekeeper::requestDomainUserFinished() {
|
||||
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||
const QJsonObject& rootObject = jsonResponse.object();
|
||||
|
||||
auto httpStatus = requestReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
|
||||
if (200 <= httpStatus && httpStatus < 300) {
|
||||
|
||||
QString username = rootObject.value("username").toString().toLower();
|
||||
if (_inFlightDomainUserIdentityRequests.contains(username)) {
|
||||
// Success! Verified user.
|
||||
_verifiedDomainUserIdentities.insert(username, _inFlightDomainUserIdentityRequests.value(username));
|
||||
_inFlightDomainUserIdentityRequests.remove(username);
|
||||
|
||||
// User user's WordPress roles as domain groups.
|
||||
QStringList domainUserGroups;
|
||||
auto userRoles = rootObject.value("roles").toArray();
|
||||
foreach (auto role, userRoles) {
|
||||
// Distinguish domain groups from metaverse groups by adding a leading special character.
|
||||
domainUserGroups.append(DOMAIN_GROUP_CHAR + role.toString().toLower());
|
||||
}
|
||||
_domainGroupMemberships[username] = domainUserGroups;
|
||||
|
||||
} else {
|
||||
// Failure.
|
||||
qDebug() << "Unexpected username in response for user details -" << username;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Failure.
|
||||
qDebug() << "Error in response for user details -" << httpStatus << requestReply->error()
|
||||
<< "-" << rootObject["error"].toString() << rootObject["error_description"].toString();
|
||||
|
||||
_inFlightDomainUserIdentityRequests.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 2015-08-24.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -29,6 +30,8 @@
|
|||
#include "NodeConnectionData.h"
|
||||
#include "PendingAssignedNodeData.h"
|
||||
|
||||
const QString DOMAIN_GROUP_CHAR = "@";
|
||||
|
||||
class DomainServer;
|
||||
|
||||
class DomainGatekeeper : public QObject {
|
||||
|
@ -71,16 +74,28 @@ public slots:
|
|||
|
||||
private slots:
|
||||
void handlePeerPingTimeout();
|
||||
|
||||
// Login and groups for domain, separate from metaverse.
|
||||
void requestDomainUserFinished();
|
||||
|
||||
private:
|
||||
SharedNodePointer processAssignmentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
const PendingAssignedNodeData& pendingAssignment);
|
||||
SharedNodePointer processAgentConnectRequest(const NodeConnectionData& nodeConnection,
|
||||
const QString& username,
|
||||
const QByteArray& usernameSignature);
|
||||
const QByteArray& usernameSignature,
|
||||
const QString& domainUsername,
|
||||
const QString& domainAccessToken,
|
||||
const QString& domainRefreshToken);
|
||||
SharedNodePointer addVerifiedNodeFromConnectRequest(const NodeConnectionData& nodeConnection);
|
||||
|
||||
bool verifyUserSignature(const QString& username, const QByteArray& usernameSignature,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
|
||||
bool needToVerifyDomainUserIdentity(const QString& username, const QString& accessToken, const QString& refreshToken);
|
||||
bool verifyDomainUserIdentity(const QString& username, const QString& accessToken, const QString& refreshToken,
|
||||
const HifiSockAddr& senderSockAddr);
|
||||
|
||||
bool isWithinMaxCapacity();
|
||||
|
||||
bool shouldAllowConnectionFromNode(const QString& username, const QByteArray& usernameSignature,
|
||||
|
@ -120,8 +135,9 @@ private:
|
|||
QSet<QString> _domainOwnerFriends; // keep track of friends of the domain owner
|
||||
QSet<QString> _inFlightGroupMembershipsRequests; // keep track of which we've already asked for
|
||||
|
||||
NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, const QHostAddress& senderAddress,
|
||||
const QString& hardwareAddress, const QUuid& machineFingerprint);
|
||||
NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, QString verifiedDomainUsername,
|
||||
const QHostAddress& senderAddress, const QString& hardwareAddress,
|
||||
const QUuid& machineFingerprint);
|
||||
|
||||
void getGroupMemberships(const QString& username);
|
||||
// void getIsGroupMember(const QString& username, const QUuid groupID);
|
||||
|
@ -133,9 +149,18 @@ private:
|
|||
using LocalIDs = std::unordered_set<Node::LocalID>;
|
||||
LocalIDs _localIDs;
|
||||
UUIDToLocalID _uuidToLocalID;
|
||||
|
||||
Node::LocalID _currentLocalID;
|
||||
Node::LocalID _idIncrement;
|
||||
|
||||
// Login and groups for domain, separate from metaverse.
|
||||
bool domainHasLogin();
|
||||
void requestDomainUser(const QString& username, const QString& accessToken, const QString& refreshToken);
|
||||
|
||||
typedef QHash<QString, QPair<QString, QString>> DomainUserIdentities; // <domainUserName, <access_token, refresh_token>>
|
||||
DomainUserIdentities _inFlightDomainUserIdentityRequests; // Domain user identity requests currently in progress.
|
||||
DomainUserIdentities _verifiedDomainUserIdentities; // Verified domain users.
|
||||
|
||||
QHash<QString, QStringList> _domainGroupMemberships; // <domainUserName, [domainGroupName]>
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
QUrl url{ MetaverseAPI::getCurrentMetaverseServerURL().toString() + metaversePath };
|
||||
|
||||
QNetworkRequest req(url);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
if (accessTokenVariant.isValid()) {
|
||||
|
@ -2458,7 +2458,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
|
|||
url.setQuery("access_token=" + accessTokenVariant.toString());
|
||||
|
||||
QNetworkRequest req(url);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().put(req, doc.toJson());
|
||||
|
||||
|
@ -2559,7 +2559,7 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u
|
|||
|
||||
QNetworkRequest tokenRequest(tokenRequestUrl);
|
||||
tokenRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
tokenRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
tokenRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
tokenRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
QNetworkReply* tokenReply = NetworkAccessManager::getInstance().post(tokenRequest, tokenPostBody.toLocal8Bit());
|
||||
|
@ -2871,7 +2871,7 @@ QNetworkReply* DomainServer::profileRequestGivenTokenReply(QNetworkReply* tokenR
|
|||
|
||||
QNetworkRequest profileRequest(profileURL);
|
||||
profileRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
profileRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
profileRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
return NetworkAccessManager::getInstance().get(profileRequest);
|
||||
}
|
||||
|
||||
|
|
|
@ -1966,6 +1966,10 @@ void DomainServerSettingsManager::apiRefreshGroupInformation() {
|
|||
QStringList groupNames = getAllKnownGroupNames();
|
||||
foreach (QString groupName, groupNames) {
|
||||
QString lowerGroupName = groupName.toLower();
|
||||
if (lowerGroupName.startsWith(DOMAIN_GROUP_CHAR)) {
|
||||
// Ignore domain groups. (Assumption: metaverse group names can't start with a "@".)
|
||||
return;
|
||||
}
|
||||
if (_groupIDs.contains(lowerGroupName)) {
|
||||
// we already know about this one. recall setGroupID in case the group has been
|
||||
// added to another section (the same group is found in both groups and blacklists).
|
||||
|
@ -2185,6 +2189,24 @@ QList<QUuid> DomainServerSettingsManager::getBlacklistGroupIDs() {
|
|||
return result.toList();
|
||||
}
|
||||
|
||||
QStringList DomainServerSettingsManager::getDomainServerGroupNames() {
|
||||
// All names as listed in the domain server settings; both metaverse groups and domain groups
|
||||
QSet<QString> result;
|
||||
foreach(NodePermissionsKey groupKey, _groupPermissions.keys()) {
|
||||
result += _groupPermissions[groupKey]->getID();
|
||||
}
|
||||
return result.toList();
|
||||
}
|
||||
|
||||
QStringList DomainServerSettingsManager::getDomainServerBlacklistGroupNames() {
|
||||
// All names as listed in the domain server settings; not necessarily mnetaverse groups.
|
||||
QSet<QString> result;
|
||||
foreach (NodePermissionsKey groupKey, _groupForbiddens.keys()) {
|
||||
result += _groupForbiddens[groupKey]->getID();
|
||||
}
|
||||
return result.toList();
|
||||
}
|
||||
|
||||
void DomainServerSettingsManager::debugDumpGroupsState() {
|
||||
qDebug() << "--------- GROUPS ---------";
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
|
||||
#include <HifiConfigVariantMap.h>
|
||||
#include <HTTPManager.h>
|
||||
|
||||
#include <ReceivedMessage.h>
|
||||
#include "NodePermissions.h"
|
||||
|
||||
#include <Node.h>
|
||||
#include <ReceivedMessage.h>
|
||||
|
||||
#include "DomainGatekeeper.h"
|
||||
#include "NodePermissions.h"
|
||||
|
||||
const QString SETTINGS_PATHS_KEY = "paths";
|
||||
|
||||
|
@ -105,6 +105,9 @@ public:
|
|||
QList<QUuid> getGroupIDs();
|
||||
QList<QUuid> getBlacklistGroupIDs();
|
||||
|
||||
QStringList getDomainServerGroupNames();
|
||||
QStringList getDomainServerBlacklistGroupNames();
|
||||
|
||||
// these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api
|
||||
void clearGroupMemberships(const QString& name) { _groupMembership[name.toLower()].clear(); }
|
||||
void recordGroupMembership(const QString& name, const QUuid groupID, QUuid rankID);
|
||||
|
|
|
@ -121,14 +121,14 @@ if (APPLE)
|
|||
# configure CMake to use a custom Info.plist
|
||||
set_target_properties(${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in)
|
||||
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "High Fidelity")
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "Vircadia")
|
||||
if (PRODUCTION_BUILD)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.interface)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.vircadia.interface)
|
||||
else ()
|
||||
if (DEV_BUILD)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.interface-dev)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.vircadia.interface-dev)
|
||||
elseif (PR_BUILD)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.highfidelity.interface-pr)
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.vircadia.interface-pr)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ MessageDialog {
|
|||
objectName: "ConnectionFailureDialog"
|
||||
|
||||
title: "No Connection"
|
||||
text: "Unable to connect to this domain. Click the 'GO TO' button on the toolbar to visit another domain."
|
||||
text: "Unable to connect to this domain. Click the 'EXPLORE' button on the toolbar to visit another domain."
|
||||
buttons: OriginalDialogs.StandardButton.Ok
|
||||
icon: OriginalDialogs.StandardIcon.Warning
|
||||
defaultButton: OriginalDialogs.StandardButton.NoButton;
|
||||
|
|
|
@ -45,6 +45,9 @@ Item {
|
|||
property bool lostFocus: false
|
||||
|
||||
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
|
||||
// If not logging into domain, then we must be logging into the metaverse...
|
||||
readonly property bool isLoggingInToDomain: loginDialog.getDomainLoginRequested()
|
||||
readonly property string domainLoginDomain: loginDialog.getDomainLoginDomain()
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
@ -71,7 +74,12 @@ Item {
|
|||
}
|
||||
|
||||
function login() {
|
||||
loginDialog.login(emailField.text, passwordField.text);
|
||||
if (!isLoggingInToDomain) {
|
||||
loginDialog.login(emailField.text, passwordField.text);
|
||||
} else {
|
||||
loginDialog.loginDomain(emailField.text, passwordField.text);
|
||||
}
|
||||
|
||||
if (linkAccountBody.loginDialogPoppedUp) {
|
||||
var data;
|
||||
if (linkAccountBody.linkSteam) {
|
||||
|
@ -87,7 +95,7 @@ Item {
|
|||
}
|
||||
bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": linkAccountBody.withSteam,
|
||||
"withOculus": linkAccountBody.withOculus, "linkSteam": linkAccountBody.linkSteam, "linkOculus": linkAccountBody.linkOculus,
|
||||
"displayName":displayNameField.text });
|
||||
"displayName":displayNameField.text, "isLoggingInToDomain": linkAccountBody.isLoggingInToDomain, "domainLoginDomain": linkAccountBody.domainLoginDomain });
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
@ -98,14 +106,22 @@ Item {
|
|||
loginErrorMessage.wrapMode = Text.WordWrap;
|
||||
errorContainer.height = (loginErrorMessageTextMetrics.width / displayNameField.width) * loginErrorMessageTextMetrics.height;
|
||||
}
|
||||
var domainLoginText = "Log In to Domain\n" + domainLoginDomain;
|
||||
loginDialogText.text = (!isLoggingInToDomain) ? "Log In to Metaverse" : domainLoginText;
|
||||
loginButton.text = (!linkAccountBody.linkSteam && !linkAccountBody.linkOculus) ? "Log In" : "Link Account";
|
||||
loginButton.text = (!isLoggingInToDomain) ? "Log In to Metaverse" : "Log In to Domain";
|
||||
loginButton.color = hifi.buttons.blue;
|
||||
displayNameField.placeholderText = "Display Name (optional)";
|
||||
var savedDisplayName = Settings.getValue("Avatar/displayName", "");
|
||||
displayNameField.text = savedDisplayName;
|
||||
emailField.placeholderText = "Username or Email";
|
||||
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
|
||||
emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : "";
|
||||
emailField.placeholderText = (!isLoggingInToDomain) ? "Username or Email" : "Username";
|
||||
if (!isLoggingInToDomain) {
|
||||
var savedUsername = Settings.getValue("keepMeLoggedIn/savedUsername", "");
|
||||
emailField.text = keepMeLoggedInCheckbox.checked ? savedUsername === "Unknown user" ? "" : savedUsername : "";
|
||||
} else {
|
||||
// ####### TODO
|
||||
}
|
||||
|
||||
if (linkAccountBody.linkSteam || linkAccountBody.linkOculus) {
|
||||
loginButton.width = (passwordField.width - hifi.dimensions.contentSpacing.x) / 2;
|
||||
loginButton.anchors.right = displayNameField.right;
|
||||
|
@ -131,7 +147,7 @@ Item {
|
|||
Item {
|
||||
id: loginContainer
|
||||
width: displayNameField.width
|
||||
height: errorContainer.height + displayNameField.height + emailField.height + passwordField.height + 5.5 * hifi.dimensions.contentSpacing.y +
|
||||
height: errorContainer.height + loginDialogTextContainer.height + displayNameField.height + emailField.height + passwordField.height + 5.5 * hifi.dimensions.contentSpacing.y +
|
||||
keepMeLoggedInCheckbox.height + loginButton.height + cantAccessTextMetrics.height + continueButton.height
|
||||
anchors {
|
||||
top: parent.top
|
||||
|
@ -145,9 +161,10 @@ Item {
|
|||
width: parent.width
|
||||
height: loginErrorMessageTextMetrics.height
|
||||
anchors {
|
||||
bottom: displayNameField.top;
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y;
|
||||
left: displayNameField.left;
|
||||
bottom: loginDialogTextContainer.top
|
||||
bottomMargin: hifi.dimensions.contentSpacing.y
|
||||
left: loginDialogTextContainer.left
|
||||
right: loginDialogTextContainer.right
|
||||
}
|
||||
TextMetrics {
|
||||
id: loginErrorMessageTextMetrics
|
||||
|
@ -160,12 +177,45 @@ Item {
|
|||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
font.bold: linkAccountBody.fontBold
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: ""
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: loginDialogTextContainer
|
||||
height: 56
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
topMargin: 1.5 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
|
||||
Text {
|
||||
id: loginDialogText
|
||||
text: qsTr("Log In")
|
||||
lineHeight: 1
|
||||
color: "white"
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
font.family: linkAccountBody.fontFamily
|
||||
font.pixelSize: 24
|
||||
font.bold: linkAccountBody.fontBold
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
HifiControlsUit.TextField {
|
||||
id: displayNameField
|
||||
|
@ -174,8 +224,8 @@ Item {
|
|||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
styleRenderType: Text.QtRendering
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: errorContainer.height
|
||||
top: loginDialogTextContainer.bottom
|
||||
topMargin: 1.5 * hifi.dimensions.contentSpacing.y
|
||||
}
|
||||
placeholderText: "Display Name (optional)"
|
||||
activeFocusOnPress: true
|
||||
|
@ -193,7 +243,11 @@ Item {
|
|||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
if (!isLoggingInToDomain) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
} else {
|
||||
// ####### TODO
|
||||
}
|
||||
}
|
||||
linkAccountBody.login();
|
||||
break;
|
||||
|
@ -232,7 +286,11 @@ Item {
|
|||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
if (!isLoggingInToDomain) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
} else {
|
||||
// ####### TODO
|
||||
}
|
||||
}
|
||||
linkAccountBody.login();
|
||||
break;
|
||||
|
@ -312,7 +370,11 @@ Item {
|
|||
case Qt.Key_Return:
|
||||
event.accepted = true;
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
if (!isLoggingInToDomain) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
} else {
|
||||
// ####### TODO
|
||||
}
|
||||
}
|
||||
linkAccountBody.login();
|
||||
break;
|
||||
|
@ -321,12 +383,13 @@ Item {
|
|||
}
|
||||
HifiControlsUit.CheckBox {
|
||||
id: keepMeLoggedInCheckbox
|
||||
checked: Settings.getValue("keepMeLoggedIn", false);
|
||||
checked: !isLoggingInToDomain ? Settings.getValue("keepMeLoggedIn", false) : false; // ####### TODO
|
||||
text: qsTr("Keep Me Logged In");
|
||||
boxSize: 18;
|
||||
labelFontFamily: linkAccountBody.fontFamily
|
||||
labelFontSize: 18;
|
||||
color: hifi.colors.white;
|
||||
visible: !isLoggingInToDomain
|
||||
anchors {
|
||||
top: passwordField.bottom;
|
||||
topMargin: hifi.dimensions.contentSpacing.y;
|
||||
|
@ -334,14 +397,22 @@ Item {
|
|||
}
|
||||
onCheckedChanged: {
|
||||
Settings.setValue("keepMeLoggedIn", checked);
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
if (!isLoggingInToDomain) {
|
||||
if (keepMeLoggedInCheckbox.checked) {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", emailField.text);
|
||||
} else {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", "");
|
||||
}
|
||||
} else {
|
||||
Settings.setValue("keepMeLoggedIn/savedUsername", "");
|
||||
// ####### TODO
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
keepMeLoggedInCheckbox.checked = !Account.loggedIn;
|
||||
if (!isLoggingInToDomain) {
|
||||
keepMeLoggedInCheckbox.checked = !Account.loggedIn;
|
||||
} else {
|
||||
// ####### TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
HifiControlsUit.Button {
|
||||
|
@ -393,7 +464,7 @@ Item {
|
|||
HifiStylesUit.ShortcutText {
|
||||
id: cantAccessText
|
||||
z: 10
|
||||
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus
|
||||
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus && !linkAccountBody.isLoggingInToDomain
|
||||
anchors {
|
||||
top: loginButton.bottom
|
||||
topMargin: hifi.dimensions.contentSpacing.y
|
||||
|
@ -403,7 +474,7 @@ Item {
|
|||
font.pixelSize: linkAccountBody.textFieldFontSize
|
||||
font.bold: linkAccountBody.fontBold
|
||||
|
||||
text: "<a href='metaverse.vircadia.com/users/password/new'> Can't access your account?</a>"
|
||||
text: "<a href='https://metaverse.vircadia.com/users/password/new'> Can't access your account?</a>"
|
||||
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
@ -492,7 +563,7 @@ Item {
|
|||
id: signUpContainer
|
||||
width: loginContainer.width
|
||||
height: signUpTextMetrics.height
|
||||
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus
|
||||
visible: !linkAccountBody.linkSteam && !linkAccountBody.linkOculus && !linkAccountBody.isLoggingInToDomain
|
||||
anchors {
|
||||
left: loginContainer.left
|
||||
top: loginContainer.bottom
|
||||
|
@ -529,7 +600,7 @@ Item {
|
|||
leftMargin: hifi.dimensions.contentSpacing.x
|
||||
}
|
||||
|
||||
text: "<a href='metaverse.vircadia.com/users/register'>Sign Up</a>"
|
||||
text: "<a href='https://metaverse.vircadia.com/users/register'>Sign Up</a>"
|
||||
|
||||
linkColor: hifi.colors.blueAccent
|
||||
onLinkActivated: {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//
|
||||
// Created by Wayne Chen on 10/18/18
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -31,6 +32,8 @@ Item {
|
|||
property bool linkSteam: linkSteam
|
||||
property bool linkOculus: linkOculus
|
||||
property bool createOculus: createOculus
|
||||
property bool isLoggingInToDomain: isLoggingInToDomain
|
||||
property string domainLoginDomain: domainLoginDomain
|
||||
property string displayName: ""
|
||||
|
||||
readonly property bool loginDialogPoppedUp: loginDialog.getLoginDialogPoppedUp()
|
||||
|
@ -106,6 +109,9 @@ Item {
|
|||
loggingInGlyph.visible = true;
|
||||
loggingInText.text = "Logging in to Oculus";
|
||||
loggingInText.x = loggingInHeader.width/2 - loggingInTextMetrics.width/2 + loggingInGlyphTextMetrics.width/2;
|
||||
} else if (loggingInBody.isLoggingInToDomain) {
|
||||
loggingInText.text = "Logging in to " + domainLoginDomain;
|
||||
loggingInText.anchors.centerIn = loggingInHeader;
|
||||
} else {
|
||||
loggingInText.text = "Logging in";
|
||||
loggingInText.anchors.centerIn = loggingInHeader;
|
||||
|
|
|
@ -137,7 +137,7 @@ Item {
|
|||
if (webViewCoreUserAgent !== undefined) {
|
||||
webViewCore.profile.httpUserAgent = webViewCoreUserAgent
|
||||
} else {
|
||||
webViewCore.profile.httpUserAgent += " (HighFidelityInterface)";
|
||||
webViewCore.profile.httpUserAgent += " (VircadiaInterface)";
|
||||
}
|
||||
// Ensure the JS from the web-engine makes it to our logging
|
||||
webViewCore.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||
|
|
|
@ -19,7 +19,7 @@ Item {
|
|||
buttons: OriginalDialogs.StandardButton.Ok,
|
||||
defaultButton: OriginalDialogs.StandardButton.NoButton,
|
||||
title: "No Connection",
|
||||
text: "Unable to connect to this domain. Click the 'GO TO' button on the toolbar to visit another domain."
|
||||
text: "Unable to connect to this domain. Click the 'EXPLORE' button on the toolbar to visit another domain."
|
||||
});
|
||||
object.selected.connect(function(button) {
|
||||
if (button === OriginalDialogs.StandardButton.Ok) {
|
||||
|
|
|
@ -336,8 +336,8 @@ Item {
|
|||
height: parent.height
|
||||
colorScheme: hifi.colorSchemes.dark
|
||||
minimumValue: 0.25
|
||||
maximumValue: 1.0
|
||||
stepSize: 0.02
|
||||
maximumValue: 2.0
|
||||
stepSize: 0.05
|
||||
value: Render.viewportResolutionScale
|
||||
live: true
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Andrzej Kapolka on 5/10/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -65,6 +66,7 @@
|
|||
#include <Trace.h>
|
||||
#include <ResourceScriptingInterface.h>
|
||||
#include <AccountManager.h>
|
||||
#include <DomainAccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <AnimDebugDraw.h>
|
||||
#include <BuildInfo.h>
|
||||
|
@ -852,6 +854,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
#else
|
||||
DependencyManager::set<AccountManager>(true, std::bind(&Application::getUserAgent, qApp));
|
||||
#endif
|
||||
DependencyManager::set<DomainAccountManager>();
|
||||
DependencyManager::set<StatTracker>();
|
||||
DependencyManager::set<ScriptEngines>(ScriptEngine::CLIENT_SCRIPT, defaultScriptsOverrideOption);
|
||||
DependencyManager::set<Preferences>();
|
||||
|
@ -1348,6 +1351,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
#endif
|
||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
|
||||
|
||||
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::authRequired, dialogsManager.data(),
|
||||
&DialogsManager::showDomainLoginDialog);
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this,
|
||||
&Application::updateWindowTitle);
|
||||
// ####### TODO: Connect any other signals from domainAccountManager.
|
||||
|
||||
// use our MyAvatar position and quat for address manager path
|
||||
addressManager->setPositionGetter([] {
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
|
@ -1571,7 +1581,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
// Do not show login dialog if requested not to on the command line
|
||||
QString hifiNoLoginCommandLineKey = QString("--").append(HIFI_NO_LOGIN_COMMAND_LINE_KEY);
|
||||
int index = arguments().indexOf(hifiNoLoginCommandLineKey);
|
||||
if (index != -1) {
|
||||
if (index != -1 || _disableLoginScreen) {
|
||||
resumeAfterLoginDialogActionTaken();
|
||||
return;
|
||||
}
|
||||
|
@ -2614,7 +2624,7 @@ QString Application::getUserAgent() {
|
|||
return userAgent;
|
||||
}
|
||||
|
||||
QString userAgent = "Mozilla/5.0 (HighFidelityInterface/" + BuildInfo::VERSION + "; "
|
||||
QString userAgent = NetworkingConstants::VIRCADIA_USER_AGENT + "/" + BuildInfo::VERSION + "; "
|
||||
+ QSysInfo::productType() + " " + QSysInfo::productVersion() + ")";
|
||||
|
||||
auto formatPluginName = [](QString name) -> QString { return name.trimmed().replace(" ", "-"); };
|
||||
|
@ -2801,6 +2811,7 @@ void Application::cleanupBeforeQuit() {
|
|||
if (!keepMeLoggedIn) {
|
||||
DependencyManager::get<AccountManager>()->removeAccountFromFile();
|
||||
}
|
||||
// ####### TODO
|
||||
|
||||
_displayPlugin.reset();
|
||||
PluginManager::getInstance()->shutdown();
|
||||
|
@ -3150,6 +3161,7 @@ extern void setupPreferences();
|
|||
static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active = false);
|
||||
#endif
|
||||
|
||||
// ####### TODO
|
||||
void Application::showLoginScreen() {
|
||||
#if !defined(DISABLE_QML)
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
@ -3939,12 +3951,15 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
|
|||
|
||||
qCDebug(interfaceapp) << "HMD:" << hasHMD << ", Hand Controllers: " << hasHandControllers << ", Using HMD: " << isUsingHMDAndHandControllers;
|
||||
|
||||
// when --url in command line, teleport to location
|
||||
const QString HIFI_URL_COMMAND_LINE_KEY = "--url";
|
||||
int urlIndex = arguments().indexOf(HIFI_URL_COMMAND_LINE_KEY);
|
||||
QString addressLookupString;
|
||||
if (urlIndex != -1) {
|
||||
QUrl url(arguments().value(urlIndex + 1));
|
||||
|
||||
// when --url in command line, teleport to location
|
||||
QCommandLineParser parser;
|
||||
QCommandLineOption urlOption("url", "", "value");
|
||||
parser.addOption(urlOption);
|
||||
parser.parse(arguments());
|
||||
if (parser.isSet(urlOption)) {
|
||||
QUrl url = QUrl(parser.value(urlOption));
|
||||
if (url.scheme() == URL_SCHEME_HIFIAPP) {
|
||||
Setting::Handle<QVariant>("startUpApp").set(url.path());
|
||||
} else {
|
||||
|
@ -7066,19 +7081,23 @@ void Application::updateWindowTitle() const {
|
|||
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
|
||||
auto isInErrorState = nodeList->getDomainHandler().isInErrorState();
|
||||
bool isMetaverseLoggedIn = accountManager->isLoggedIn();
|
||||
bool isDomainLoggedIn = domainAccountManager->isLoggedIn();
|
||||
QString authedDomain = domainAccountManager->getAuthedDomain();
|
||||
|
||||
QString buildVersion = " - Vircadia - "
|
||||
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
|
||||
+ " " + applicationVersion();
|
||||
|
||||
QString loginStatus = accountManager->isLoggedIn() ? "" : " (NOT LOGGED IN)";
|
||||
|
||||
QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" :
|
||||
nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)";
|
||||
QString username = accountManager->getAccountInfo().getUsername();
|
||||
|
||||
setCrashAnnotation("sentry[user][username]", username.toStdString());
|
||||
QString metaverseUsername = accountManager->getAccountInfo().getUsername();
|
||||
QString domainUsername = domainAccountManager->getUsername();
|
||||
|
||||
setCrashAnnotation("sentry[user][username]", metaverseUsername.toStdString());
|
||||
|
||||
QString currentPlaceName;
|
||||
if (isServerlessMode()) {
|
||||
|
@ -7094,8 +7113,22 @@ void Application::updateWindowTitle() const {
|
|||
}
|
||||
}
|
||||
|
||||
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
|
||||
+ currentPlaceName + connectionStatus + loginStatus + buildVersion;
|
||||
QString metaverseDetails;
|
||||
if (isMetaverseLoggedIn) {
|
||||
metaverseDetails = "Metaverse: Logged in as " + metaverseUsername;
|
||||
} else {
|
||||
metaverseDetails = "Metaverse: Not Logged In";
|
||||
}
|
||||
|
||||
QString domainDetails;
|
||||
if (currentPlaceName == authedDomain && isDomainLoggedIn) {
|
||||
domainDetails = "Domain: Logged in as " + domainUsername;
|
||||
} else {
|
||||
domainDetails = "Domain: Not Logged In";
|
||||
}
|
||||
|
||||
QString title = QString() + currentPlaceName + connectionStatus + " (" + metaverseDetails + ") (" + domainDetails + ")"
|
||||
+ buildVersion;
|
||||
|
||||
#ifndef WIN32
|
||||
// crashes with vs2013/win32
|
||||
|
@ -7655,7 +7688,7 @@ bool Application::askToWearAvatarAttachmentUrl(const QString& url) {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(url);
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
||||
int requestNumber = ++_avatarAttachmentRequest;
|
||||
connect(reply, &QNetworkReply::finished, [this, reply, url, requestNumber]() {
|
||||
|
|
|
@ -733,6 +733,7 @@ private:
|
|||
GraphicsEngine _graphicsEngine;
|
||||
void updateRenderArgs(float deltaTime);
|
||||
|
||||
bool _disableLoginScreen { true };
|
||||
|
||||
Overlays _overlays;
|
||||
ApplicationOverlay _applicationOverlay;
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
#include "Application.h"
|
||||
#include "ui/DialogsManager.h"
|
||||
|
||||
#include <AccountManager.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <DomainHandler.h>
|
||||
#include <DomainAccountManager.h>
|
||||
#include <AddressManager.h>
|
||||
#include <NodeList.h>
|
||||
|
||||
|
@ -34,6 +36,10 @@ void ConnectionMonitor::init() {
|
|||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ConnectionMonitor::stopTimer);
|
||||
connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &ConnectionMonitor::stopTimer);
|
||||
connect(this, &ConnectionMonitor::setRedirectErrorState, &domainHandler, &DomainHandler::setRedirectErrorState);
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
connect(accountManager.data(), &AccountManager::loginComplete, this, &ConnectionMonitor::startTimer);
|
||||
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this, &ConnectionMonitor::startTimer);
|
||||
|
||||
_timer.setSingleShot(true);
|
||||
if (!domainHandler.isConnected()) {
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
// For happ(ier) development of QML, use these two things:
|
||||
// This forces QML files to be pulled from the source as you edit it: set environment variable HIFI_USE_SOURCE_TREE_RESOURCES=1
|
||||
// Use this to live reload: DependencyManager::get<OffscreenUi>()->clearCache();
|
||||
|
||||
#include "Menu.h"
|
||||
#include <QDesktopServices>
|
||||
#include <QFileDialog>
|
||||
|
@ -84,6 +88,13 @@ Menu::Menu() {
|
|||
dialogsManager.data(), &DialogsManager::toggleLoginDialog);
|
||||
}
|
||||
|
||||
auto domainLogin = addActionToQMenuAndActionHash(fileMenu, "Domain: Log In");
|
||||
connect(domainLogin, &QAction::triggered, [] {
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->setDomainLoginState();
|
||||
dialogsManager->showDomainLoginDialog();
|
||||
});
|
||||
|
||||
// File > Quit
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp, SLOT(quit()), QAction::QuitRole);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ void PerformanceManager::setupPerformancePresetSettings(bool evaluatePlatformTie
|
|||
// If evaluatePlatformTier, evalute the Platform Tier and assign the matching Performance profile by default.
|
||||
// A bunch of Performance, Simulation and Render settings will be set to a matching default value from this
|
||||
|
||||
// Here is the mapping between pelatformTIer and performance profile
|
||||
// Here is the mapping between platformTier and performance profile
|
||||
const std::array<PerformanceManager::PerformancePreset, platform::Profiler::NumTiers> platformToPerformancePresetMap = { {
|
||||
PerformanceManager::PerformancePreset::MID, // platform::Profiler::UNKNOWN
|
||||
PerformanceManager::PerformancePreset::LOW, // platform::Profiler::LOW
|
||||
|
|
|
@ -722,7 +722,7 @@ public:
|
|||
* @function MyAvatar.restoreHandAnimation
|
||||
* @param isLeft {boolean} Set to true if using the left hand
|
||||
* @example <caption> Override left hand animation for three seconds. </caption>
|
||||
* var ANIM_URL = "https://apidocs.projectathena.dev/models/ClapHands_Standing.fbx";
|
||||
* var ANIM_URL = "https://apidocs.vircadia.dev/models/ClapHands_Standing.fbx";
|
||||
* MyAvatar.overrideHandAnimation(isLeft, ANIM_URL, 30, true, 0, 53);
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.restoreHandAnimation();
|
||||
|
@ -780,7 +780,7 @@ public:
|
|||
* hanging at its sides when it is not moving, the avatar will stand and clap its hands. Note that just as it did before, as soon as the avatar
|
||||
* starts to move, the animation will smoothly blend into the walk animation used by the "walkFwd" animation role.</caption>
|
||||
* // An animation of the avatar clapping its hands while standing. Restore default after 30s.
|
||||
* var ANIM_URL = "https://apidocs.projectathena.dev/models/ClapHands_Standing.fbx";
|
||||
* var ANIM_URL = "https://apidocs.vircadia.dev/models/ClapHands_Standing.fbx";
|
||||
* MyAvatar.overrideRoleAnimation("idleStand", ANIM_URL, 30, true, 0, 53);
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.restoreRoleAnimation();
|
||||
|
|
|
@ -407,13 +407,13 @@ void MyCharacterController::clearDetailedMotionStates() {
|
|||
}
|
||||
|
||||
void MyCharacterController::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
_detailedMotionStates[i]->forceActive();
|
||||
for (auto& detailedMotionState : _detailedMotionStates) {
|
||||
detailedMotionState->forceActive();
|
||||
}
|
||||
if (_pendingFlags & PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION) {
|
||||
_pendingFlags &= ~PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION;
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
transaction.objectsToRemove.push_back(_detailedMotionStates[i]);
|
||||
for (auto& detailedMotionState : _detailedMotionStates) {
|
||||
transaction.objectsToRemove.push_back(detailedMotionState);
|
||||
}
|
||||
// NOTE: the DetailedMotionStates are deleted after being added to PhysicsEngine::Transaction::_objectsToRemove
|
||||
// See AvatarManager::handleProcessedPhysicsTransaction()
|
||||
|
|
|
@ -116,10 +116,10 @@ void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const {
|
|||
|
||||
int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) {
|
||||
int32_t bytesRead = Avatar::parseDataFromBuffer(buffer);
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
for (auto& detailedMotionState : _detailedMotionStates) {
|
||||
// NOTE: we activate _detailedMotionStates is because they are KINEMATIC
|
||||
// and Bullet will automagically call DetailedMotionState::getWorldTransform() when active.
|
||||
_detailedMotionStates[i]->forceActive();
|
||||
detailedMotionState->forceActive();
|
||||
}
|
||||
if (_moving && _motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "Menu.h"
|
||||
#include "OffscreenUi.h"
|
||||
#include "commerce/QmlCommerce.h"
|
||||
#include "NetworkingConstants.h"
|
||||
|
||||
static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
static const QString LAST_BROWSE_LOCATION_SETTING = "LastBrowseLocation";
|
||||
|
@ -411,6 +412,10 @@ QString WindowScriptingInterface::checkVersion() {
|
|||
return QCoreApplication::applicationVersion();
|
||||
}
|
||||
|
||||
QString WindowScriptingInterface::getUserAgent() {
|
||||
return NetworkingConstants::VIRCADIA_USER_AGENT;
|
||||
}
|
||||
|
||||
QString WindowScriptingInterface::protocolSignature() {
|
||||
return protocolVersionsSignatureBase64();
|
||||
}
|
||||
|
|
|
@ -295,6 +295,13 @@ public slots:
|
|||
*/
|
||||
QString checkVersion();
|
||||
|
||||
/**jsdoc
|
||||
* Gets Interface's user agent.
|
||||
* @function Window.getUserAgent
|
||||
* @returns {string} Interface's user agent.
|
||||
*/
|
||||
QString getUserAgent();
|
||||
|
||||
/**jsdoc
|
||||
* Gets the signature for Interface's protocol version.
|
||||
* @function Window.protocolSignature
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Clement on 1/18/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -109,11 +110,34 @@ void DialogsManager::setDomainConnectionFailureVisibility(bool visible) {
|
|||
}
|
||||
}
|
||||
|
||||
void DialogsManager::setMetaverseLoginState() {
|
||||
// We're only turning off the domain login trigger but the actual domain auth URL is still saved.
|
||||
// So we can continue the domain login if desired.
|
||||
_isDomainLogin = false;
|
||||
}
|
||||
|
||||
void DialogsManager::setDomainLoginState() {
|
||||
_isDomainLogin = true;
|
||||
}
|
||||
|
||||
void DialogsManager::setDomainLogin(bool isDomainLogin, const QString& domain) {
|
||||
_isDomainLogin = isDomainLogin;
|
||||
if (!domain.isEmpty()) {
|
||||
_domainLoginDomain = domain;
|
||||
}
|
||||
}
|
||||
|
||||
void DialogsManager::toggleLoginDialog() {
|
||||
setDomainLogin(false);
|
||||
LoginDialog::toggleAction();
|
||||
}
|
||||
|
||||
void DialogsManager::showLoginDialog() {
|
||||
|
||||
// ####### TODO: May be called from script via DialogsManagerScriptingInterface. Need to handle the case that it's already
|
||||
// displayed and may be the domain login version.
|
||||
|
||||
setDomainLogin(false);
|
||||
LoginDialog::showWithSelection();
|
||||
}
|
||||
|
||||
|
@ -121,10 +145,22 @@ void DialogsManager::hideLoginDialog() {
|
|||
LoginDialog::hide();
|
||||
}
|
||||
|
||||
|
||||
void DialogsManager::showDomainLoginDialog(const QString& domain) {
|
||||
setDomainLogin(true, domain);
|
||||
LoginDialog::showWithSelection();
|
||||
}
|
||||
|
||||
// #######: TODO: Domain version of toggleLoginDialog()?
|
||||
|
||||
// #######: TODO: Domain version of hideLoginDialog()?
|
||||
|
||||
|
||||
void DialogsManager::showUpdateDialog() {
|
||||
UpdateDialog::show();
|
||||
}
|
||||
|
||||
|
||||
void DialogsManager::octreeStatsDetails() {
|
||||
if (!_octreeStatsDialog) {
|
||||
_octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats());
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Clement on 1/18/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -40,6 +41,10 @@ public:
|
|||
QPointer<TestingDialog> getTestingDialog() const { return _testingDialog; }
|
||||
void emitAddressBarShown(bool visible) { emit addressBarShown(visible); }
|
||||
void setAddressBarVisible(bool addressBarVisible);
|
||||
void setMetaverseLoginState();
|
||||
void setDomainLoginState();
|
||||
bool getIsDomainLogin() { return _isDomainLogin; }
|
||||
QString getDomainLoginDomain() { return _domainLoginDomain; }
|
||||
|
||||
public slots:
|
||||
void showAddressBar();
|
||||
|
@ -49,6 +54,7 @@ public slots:
|
|||
void toggleLoginDialog();
|
||||
void showLoginDialog();
|
||||
void hideLoginDialog();
|
||||
void showDomainLoginDialog(const QString& domain = "");
|
||||
void octreeStatsDetails();
|
||||
void lodTools();
|
||||
void hmdTools(bool showTools);
|
||||
|
@ -82,6 +88,10 @@ private:
|
|||
QPointer<DomainConnectionDialog> _domainConnectionDialog;
|
||||
bool _dialogCreatedWhileShown { false };
|
||||
bool _addressBarVisible { false };
|
||||
|
||||
void setDomainLogin(bool isDomainLogin, const QString& domain = "");
|
||||
bool _isDomainLogin { false };
|
||||
QString _domainLoginDomain;
|
||||
};
|
||||
|
||||
#endif // hifi_DialogsManager_h
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/04/14
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -24,7 +25,9 @@
|
|||
#include <UserActivityLogger.h>
|
||||
|
||||
#include "AccountManager.h"
|
||||
#include "DomainAccountManager.h"
|
||||
#include "DependencyManager.h"
|
||||
#include "DialogsManager.h"
|
||||
#include "Menu.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
@ -38,12 +41,17 @@ const QUrl LOGIN_DIALOG = PathUtils::qmlUrl("OverlayLoginDialog.qml");
|
|||
|
||||
LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) {
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
|
||||
// the login hasn't been dismissed yet if the user isn't logged in and is encouraged to login.
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
connect(accountManager.data(), &AccountManager::loginComplete,
|
||||
this, &LoginDialog::handleLoginCompleted);
|
||||
connect(accountManager.data(), &AccountManager::loginFailed,
|
||||
this, &LoginDialog::handleLoginFailed);
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete,
|
||||
this, &LoginDialog::handleLoginCompleted);
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::loginFailed,
|
||||
this, &LoginDialog::handleLoginFailed);
|
||||
connect(qApp, &Application::loginDialogFocusEnabled, this, &LoginDialog::focusEnabled);
|
||||
connect(qApp, &Application::loginDialogFocusDisabled, this, &LoginDialog::focusDisabled);
|
||||
connect(this, SIGNAL(dismissedLoginDialog()), qApp, SLOT(onDismissedLoginDialog()));
|
||||
|
@ -91,14 +99,16 @@ void LoginDialog::toggleAction() {
|
|||
|
||||
if (accountManager->isLoggedIn()) {
|
||||
// change the menu item to logout
|
||||
loginAction->setText("Logout " + accountManager->getAccountInfo().getUsername());
|
||||
loginAction->setText("Metaverse: Logout " + accountManager->getAccountInfo().getUsername());
|
||||
connection = connect(loginAction, &QAction::triggered, accountManager.data(), &AccountManager::logout);
|
||||
} else {
|
||||
// change the menu item to login
|
||||
loginAction->setText("Log In / Sign Up");
|
||||
loginAction->setText("Metaverse: Log In / Sign Up");
|
||||
connection = connect(loginAction, &QAction::triggered, [] {
|
||||
// if not in login state, show.
|
||||
if (!qApp->getLoginDialogPoppedUp()) {
|
||||
auto dialogsManager = DependencyManager::get<DialogsManager>();
|
||||
dialogsManager->setMetaverseLoginState();
|
||||
LoginDialog::showWithSelection();
|
||||
}
|
||||
});
|
||||
|
@ -131,10 +141,15 @@ void LoginDialog::dismissLoginDialog() {
|
|||
}
|
||||
|
||||
void LoginDialog::login(const QString& username, const QString& password) const {
|
||||
qDebug() << "Attempting to login " << username;
|
||||
qDebug() << "Attempting to login" << username;
|
||||
DependencyManager::get<AccountManager>()->requestAccessToken(username, password);
|
||||
}
|
||||
|
||||
void LoginDialog::loginDomain(const QString& username, const QString& password) const {
|
||||
qDebug() << "Attempting to login" << username << "into a domain";
|
||||
DependencyManager::get<DomainAccountManager>()->requestAccessToken(username, password);
|
||||
}
|
||||
|
||||
void LoginDialog::loginThroughOculus() {
|
||||
qDebug() << "Attempting to login through Oculus";
|
||||
if (auto oculusPlatformPlugin = PluginManager::getInstance()->getOculusPlatformPlugin()) {
|
||||
|
@ -410,3 +425,11 @@ void LoginDialog::signupFailed(QNetworkReply* reply) {
|
|||
emit handleSignupFailed(DEFAULT_SIGN_UP_FAILURE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
bool LoginDialog::getDomainLoginRequested() const {
|
||||
return DependencyManager::get<DialogsManager>()->getIsDomainLogin();
|
||||
}
|
||||
|
||||
QString LoginDialog::getDomainLoginDomain() const {
|
||||
return DependencyManager::get<DialogsManager>()->getDomainLoginDomain();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2015/04/14
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -71,6 +72,7 @@ protected slots:
|
|||
Q_INVOKABLE QString oculusUserID() const;
|
||||
|
||||
Q_INVOKABLE void login(const QString& username, const QString& password) const;
|
||||
Q_INVOKABLE void loginDomain(const QString& username, const QString& password) const;
|
||||
Q_INVOKABLE void loginThroughSteam();
|
||||
Q_INVOKABLE void linkSteam();
|
||||
Q_INVOKABLE void createAccountFromSteam(QString username = QString());
|
||||
|
@ -81,6 +83,10 @@ protected slots:
|
|||
Q_INVOKABLE void signup(const QString& email, const QString& username, const QString& password);
|
||||
|
||||
Q_INVOKABLE bool getLoginDialogPoppedUp() const;
|
||||
|
||||
Q_INVOKABLE bool getDomainLoginRequested() const;
|
||||
Q_INVOKABLE QString getDomainLoginDomain() const;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_LoginDialog_h
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <ThreadHelpers.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons", "attachments" };
|
||||
|
@ -225,7 +226,7 @@ void ModelHandler::update() {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.head(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||
}
|
||||
|
@ -278,7 +279,7 @@ void ModelHandler::queryNewFiles(QString marker) {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||
|
||||
|
|
|
@ -143,13 +143,13 @@ set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR})
|
|||
include(CPackComponent)
|
||||
|
||||
set(CPACK_PACKAGE_NAME "HQ Launcher")
|
||||
set(CPACK_PACKAGE_VENDOR "High Fidelity")
|
||||
set(CPACK_PACKAGE_VENDOR "Vircadia")
|
||||
set(CPACK_PACKAGE_VERSION ${BUILD_VERSION})
|
||||
set(CPACK_PACKAGE_FILE_NAME "HQ Launcher")
|
||||
|
||||
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
|
||||
|
||||
set(DMG_SUBFOLDER_NAME "High Fidelity")
|
||||
set(DMG_SUBFOLDER_NAME "Vircadia")
|
||||
set(ESCAPED_DMG_SUBFOLDER_NAME "")
|
||||
set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc")
|
||||
|
||||
|
|
|
@ -276,12 +276,12 @@ if (APPLE)
|
|||
include(CPackComponent)
|
||||
|
||||
set(CPACK_PACKAGE_NAME "HQ Launcher")
|
||||
set(CPACK_PACKAGE_VENDOR "High Fidelity")
|
||||
set(CPACK_PACKAGE_VENDOR "Vircadia")
|
||||
set(CPACK_PACKAGE_FILE_NAME "HQ Launcher")
|
||||
|
||||
set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
|
||||
|
||||
set(DMG_SUBFOLDER_NAME "High Fidelity")
|
||||
set(DMG_SUBFOLDER_NAME "Vircadia")
|
||||
set(ESCAPED_DMG_SUBFOLDER_NAME "")
|
||||
set(DMG_SUBFOLDER_ICON "${CMAKE_SOURCE_DIR}/cmake/installer/install-folder.rsrc")
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import QtQuick 2.3
|
|||
import QtQuick 2.1
|
||||
|
||||
Text {
|
||||
text: "High Fidelity"
|
||||
text: "Vircadia"
|
||||
font.bold: true
|
||||
font.family: "Graphik Semibold"
|
||||
font.pixelSize: 17
|
||||
|
|
|
@ -21,7 +21,7 @@ Launcher::Launcher(int& argc, char**argv) : QGuiApplication(argc, argv) {
|
|||
_launcherWindow->rootContext()->setContextProperty("LauncherState", _launcherState.get());
|
||||
_launcherWindow->rootContext()->setContextProperty("PathUtils", new PathUtils());
|
||||
_launcherWindow->rootContext()->setContextProperty("Platform", platform);
|
||||
_launcherWindow->setTitle("High Fidelity");
|
||||
_launcherWindow->setTitle("Vircadia");
|
||||
_launcherWindow->setFlags(Qt::FramelessWindowHint | Qt::Window);
|
||||
_launcherWindow->setLauncherStatePtr(_launcherState);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <QStandardPaths>
|
||||
#include <QFileInfo>
|
||||
#include <QFile>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QTime>
|
||||
|
||||
|
@ -253,7 +253,7 @@ void LauncherInstaller::createApplicationRegistryKeys() {
|
|||
success = insertRegistryKey(REGISTRY_PATH, "UninstallString", uninstallPath);
|
||||
success = insertRegistryKey(REGISTRY_PATH, "DisplayVersion", std::string(LAUNCHER_BUILD_VERSION));
|
||||
success = insertRegistryKey(REGISTRY_PATH, "DisplayIcon", applicationExe);
|
||||
success = insertRegistryKey(REGISTRY_PATH, "Publisher", "High Fidelity");
|
||||
success = insertRegistryKey(REGISTRY_PATH, "Publisher", "Vircadia");
|
||||
|
||||
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ bool hasSuffix(const std::string& path, const std::string& suffix) {
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QCoreApplication::setOrganizationName("High Fidelity");
|
||||
QCoreApplication::setOrganizationName("Vircadia");
|
||||
QCoreApplication::setApplicationName("HQ Launcher");
|
||||
|
||||
Q_INIT_RESOURCE(resources);
|
||||
|
|
|
@ -310,7 +310,7 @@ BOOL LauncherManager::getAndCreatePaths(PathType type, CString& outPath) {
|
|||
outPath += DIRECTORY_NAME_INTERFACE;
|
||||
} else if (type == PathType::Content_Directory) {
|
||||
outPath += DIRECTORY_NAME_CONTENT;
|
||||
}
|
||||
}
|
||||
return (CreateDirectory(outPath, NULL) || ERROR_ALREADY_EXISTS == GetLastError());
|
||||
}
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ BOOL LauncherManager::createConfigJSON() {
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, CString& domain,
|
||||
LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, CString& domain,
|
||||
CString& content, bool& loggedIn, CString& organizationBuildTag) {
|
||||
CString configPath;
|
||||
getAndCreatePaths(PathType::Interface_Directory, configPath);
|
||||
|
@ -388,7 +388,7 @@ LauncherUtils::ResponseError LauncherManager::readConfigJSON(CString& version, C
|
|||
}
|
||||
Json::Value config;
|
||||
configFile >> config;
|
||||
if (config["version"].isString() &&
|
||||
if (config["version"].isString() &&
|
||||
config["domain"].isString() &&
|
||||
config["content"].isString()) {
|
||||
loggedIn = config["loggedIn"].asBool();
|
||||
|
@ -446,7 +446,7 @@ LauncherUtils::ResponseError LauncherManager::readOrganizationJSON(const CString
|
|||
CString url = _T("/organizations/") + hash + _T(".json");
|
||||
LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(getHttpUserAgent(),
|
||||
true, L"orgs.highfidelity.com", url,
|
||||
contentTypeJson, CStringA(),
|
||||
contentTypeJson, CStringA(),
|
||||
response, false);
|
||||
if (error != LauncherUtils::ResponseError::NoError) {
|
||||
return error;
|
||||
|
@ -557,7 +557,7 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch
|
|||
addToLog(_T("Already running most recent build. Launching interface.exe"));
|
||||
} else {
|
||||
addToLog(_T("Updating the launcher was not allowed --noUpdate"));
|
||||
}
|
||||
}
|
||||
if (isInstalled) {
|
||||
addToLog(_T("Installed version: ") + currentVersion);
|
||||
if (!newInterfaceVersion) {
|
||||
|
@ -576,7 +576,7 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch
|
|||
}
|
||||
}
|
||||
_shouldWait = FALSE;
|
||||
|
||||
|
||||
} else {
|
||||
setFailed(true);
|
||||
CString msg;
|
||||
|
@ -587,7 +587,7 @@ void LauncherManager::onMostRecentBuildsReceived(const CString& response, Launch
|
|||
}
|
||||
}
|
||||
|
||||
LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const CString& username,
|
||||
LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const CString& username,
|
||||
const CString& password) {
|
||||
CStringA post = "grant_type=password&username=";
|
||||
post += username;
|
||||
|
@ -599,9 +599,9 @@ LauncherUtils::ResponseError LauncherManager::getAccessTokenForCredentials(const
|
|||
CString response;
|
||||
LauncherUtils::ResponseError error = LauncherUtils::makeHTTPCall(getHttpUserAgent(),
|
||||
true,
|
||||
L"metaverse.highfidelity.com",
|
||||
L"metaverse.highfidelity.com",
|
||||
L"/oauth/token",
|
||||
contentTypeText, post,
|
||||
contentTypeText, post,
|
||||
response, true);
|
||||
if (error != LauncherUtils::ResponseError::NoError) {
|
||||
return error;
|
||||
|
@ -629,7 +629,7 @@ BOOL LauncherManager::createApplicationRegistryKeys(int size) {
|
|||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "UninstallString", uninstallPath);
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayVersion", LauncherUtils::cStringToStd(_latestVersion));
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "DisplayIcon", applicationExe);
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "Publisher", "High Fidelity");
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "Publisher", "Vircadia");
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "InstallDate", LauncherUtils::cStringToStd(CTime::GetCurrentTime().Format("%Y%m%d")));
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "EstimatedSize", (DWORD)size);
|
||||
success = LauncherUtils::insertRegistryKey(REGISTRY_PATH, "NoModify", (DWORD)1);
|
||||
|
@ -686,9 +686,9 @@ BOOL LauncherManager::extractApplication() {
|
|||
updateProgress(ProcessType::UnzipApplication, max(progress, 0.0f));
|
||||
};
|
||||
_currentProcess = ProcessType::UnzipApplication;
|
||||
BOOL success = LauncherUtils::unzipFileOnThread(ProcessType::UnzipApplication,
|
||||
BOOL success = LauncherUtils::unzipFileOnThread(ProcessType::UnzipApplication,
|
||||
LauncherUtils::cStringToStd(_applicationZipPath),
|
||||
LauncherUtils::cStringToStd(installPath),
|
||||
LauncherUtils::cStringToStd(installPath),
|
||||
onExtractFinished, onProgress);
|
||||
if (success) {
|
||||
addToLog(_T("Created thread for unzipping application."));
|
||||
|
@ -737,7 +737,7 @@ void LauncherManager::restartNewLauncher() {
|
|||
continueAction = ContinueActionOnStart::ContinueUpdate;
|
||||
} else if (_keepLoggingIn) {
|
||||
continueAction = ContinueActionOnStart::ContinueLogIn;
|
||||
}
|
||||
}
|
||||
CStringW params;
|
||||
params.Format(_T(" --restart --noUpdate --continueAction %s"), getContinueActionParam(continueAction));
|
||||
LauncherUtils::launchApplication(_tempLauncherPath, params.GetBuffer());
|
||||
|
|
|
@ -51,7 +51,7 @@ void AutoUpdater::getLatestVersionData() {
|
|||
QNetworkRequest latestVersionRequest(buildsURL);
|
||||
|
||||
latestVersionRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(latestVersionRequest);
|
||||
connect(reply, &QNetworkReply::finished, this, &AutoUpdater::parseLatestVersionData);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
|
@ -62,7 +63,7 @@ void JSBaker::loadScript() {
|
|||
// setup the request to follow re-directs and always hit the network
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
|
||||
networkRequest.setUrl(_jsURL);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <PathUtils.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <hfm/ModelFormatRegistry.h>
|
||||
|
@ -159,7 +160,7 @@ void ModelBaker::saveSourceModel() {
|
|||
// setup the request to follow re-directs and always hit the network
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
|
||||
networkRequest.setUrl(_modelURL);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <image/TextureProcessing.h>
|
||||
#include <ktx/KTX.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextureMeta.h>
|
||||
|
||||
|
@ -99,7 +100,7 @@ void TextureBaker::loadTexture() {
|
|||
// setup the request to follow re-directs and always hit the network
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
|
||||
networkRequest.setUrl(_textureURL);
|
||||
|
||||
|
|
|
@ -1337,7 +1337,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
_model->setCullWithParent(_cullWithParent);
|
||||
_model->setRenderWithZones(_renderWithZones);
|
||||
emit requestRenderUpdate();
|
||||
if(didVisualGeometryRequestSucceed) {
|
||||
if (didVisualGeometryRequestSucceed) {
|
||||
emit DependencyManager::get<scriptable::ModelProviderFactory>()->
|
||||
modelAddedToScene(entity->getEntityItemID(), NestableType::Entity, _model);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ private:
|
|||
PulsePropertyGroup _pulseProperties;
|
||||
std::shared_ptr<graphics::ProceduralMaterial> _material { std::make_shared<graphics::ProceduralMaterial>() };
|
||||
glm::vec3 _color { NAN };
|
||||
float _alpha;
|
||||
float _alpha { NAN };
|
||||
|
||||
glm::vec3 _position;
|
||||
glm::vec3 _dimensions;
|
||||
|
|
|
@ -2793,6 +2793,17 @@ bool EntityTree::sendEntitiesOperation(const OctreeElementPointer& element, void
|
|||
}
|
||||
}
|
||||
|
||||
QVector<QUuid> oldRenderWithZones = properties.getRenderWithZones();
|
||||
if (!oldRenderWithZones.isEmpty()) {
|
||||
QVector<QUuid> newRenderWithZones;
|
||||
for (QUuid oldRenderWithZoneID : oldRenderWithZones) {
|
||||
if (args->ourTree->findEntityByEntityItemID(oldRenderWithZoneID)) {
|
||||
newRenderWithZones.append(getMapped(oldRenderWithZoneID));
|
||||
}
|
||||
}
|
||||
properties.setRenderWithZones(newRenderWithZones);
|
||||
}
|
||||
|
||||
properties.setXNNeighborID(getMapped(properties.getXNNeighborID()));
|
||||
properties.setXPNeighborID(getMapped(properties.getXPNeighborID()));
|
||||
properties.setYNNeighborID(getMapped(properties.getYNNeighborID()));
|
||||
|
|
|
@ -127,7 +127,7 @@ void appendIndex(MeshData& data, QVector<int>& indices, int index, bool deduplic
|
|||
|
||||
|
||||
glm::vec4 color;
|
||||
bool hasColors = (data.colors.size() > 1);
|
||||
bool hasColors = (data.colors.size() > 0);
|
||||
if (hasColors) {
|
||||
int colorIndex = data.colorsByVertex ? vertexIndex : index;
|
||||
if (data.colorIndices.isEmpty()) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QNetworkRequest>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
QVariantHash FSTReader::parseMapping(QIODevice* device) {
|
||||
|
@ -253,7 +254,7 @@ QVariantHash FSTReader::downloadMapping(const QString& url) {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(url);
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f
|
||||
#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f
|
||||
#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024)
|
||||
#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)2048)
|
||||
#define MAX_RESOURCE_TEXTURES_PER_FRAME 2
|
||||
#define NO_BUFFER_WORK_SLEEP_TIME_MS 2
|
||||
#define THREADED_TEXTURE_BUFFERING 1
|
||||
|
|
|
@ -508,7 +508,9 @@ bool AccountManager::checkAndSignalForAccessToken() {
|
|||
|
||||
if (!hasToken) {
|
||||
// emit a signal so somebody can call back to us and request an access token given a username and password
|
||||
emit authRequired();
|
||||
|
||||
// Dialog can be hidden immediately after showing if we've just teleported to the domain, unless the signal is delayed.
|
||||
QTimer::singleShot(500, this, [this] { emit this->authRequired(); });
|
||||
}
|
||||
|
||||
return hasToken;
|
||||
|
|
|
@ -58,7 +58,7 @@ const auto METAVERSE_SESSION_ID_HEADER = QString("HFM-SessionID").toLocal8Bit();
|
|||
|
||||
using UserAgentGetter = std::function<QString()>;
|
||||
|
||||
const auto DEFAULT_USER_AGENT_GETTER = []() -> QString { return HIGH_FIDELITY_USER_AGENT; };
|
||||
const auto DEFAULT_USER_AGENT_GETTER = []() -> QString { return NetworkingConstants::VIRCADIA_USER_AGENT; };
|
||||
|
||||
class AccountManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
|
203
libraries/networking/src/DomainAccountManager.cpp
Normal file
203
libraries/networking/src/DomainAccountManager.cpp
Normal file
|
@ -0,0 +1,203 @@
|
|||
//
|
||||
// DomainAccountManager.cpp
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by David Rowe on 23 Jul 2020.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "DomainAccountManager.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <SettingHandle.h>
|
||||
|
||||
#include "NetworkingConstants.h"
|
||||
#include "NetworkAccessManager.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include "NodeList.h"
|
||||
|
||||
// FIXME: Generalize to other OAuth2 sources for domain login.
|
||||
|
||||
const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false;
|
||||
|
||||
// ####### TODO: Enable and use these?
|
||||
// ####### TODO: Add storing domain URL and check against it when retrieving values?
|
||||
// ####### TODO: Add storing _authURL and check against it when retrieving values?
|
||||
/*
|
||||
Setting::Handle<QString> domainAccessToken {"private/domainAccessToken", "" };
|
||||
Setting::Handle<QString> domainAccessRefreshToken {"private/domainAccessToken", "" };
|
||||
Setting::Handle<int> domainAccessTokenExpiresIn {"private/domainAccessTokenExpiresIn", -1 };
|
||||
Setting::Handle<QString> domainAccessTokenType {"private/domainAccessTokenType", "" };
|
||||
*/
|
||||
|
||||
DomainAccountManager::DomainAccountManager() :
|
||||
_authURL(),
|
||||
_username(),
|
||||
_access_token(),
|
||||
_refresh_token(),
|
||||
_domain_name()
|
||||
{
|
||||
connect(this, &DomainAccountManager::loginComplete, this, &DomainAccountManager::sendInterfaceAccessTokenToServer);
|
||||
}
|
||||
|
||||
void DomainAccountManager::setAuthURL(const QUrl& authURL) {
|
||||
if (_authURL != authURL) {
|
||||
_authURL = authURL;
|
||||
|
||||
qCDebug(networking) << "AccountManager URL for authenticated requests has been changed to" << qPrintable(_authURL.toString());
|
||||
|
||||
_access_token = "";
|
||||
_refresh_token = "";
|
||||
|
||||
// ####### TODO: Restore and refresh OAuth2 tokens if have them for this domain.
|
||||
|
||||
// ####### TODO: Handle "keep me logged in".
|
||||
}
|
||||
}
|
||||
|
||||
bool DomainAccountManager::isLoggedIn() {
|
||||
return !_authURL.isEmpty() && hasValidAccessToken();
|
||||
}
|
||||
|
||||
void DomainAccountManager::requestAccessToken(const QString& username, const QString& password) {
|
||||
|
||||
_username = username;
|
||||
_access_token = "";
|
||||
_refresh_token = "";
|
||||
|
||||
QNetworkRequest request;
|
||||
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
|
||||
// miniOrange WordPress API Authentication plugin:
|
||||
// - Requires "client_id" parameter.
|
||||
// - Ignores "state" parameter.
|
||||
QByteArray formData;
|
||||
formData.append("grant_type=password&");
|
||||
formData.append("username=" + QUrl::toPercentEncoding(username) + "&");
|
||||
formData.append("password=" + QUrl::toPercentEncoding(password) + "&");
|
||||
formData.append("client_id=" + _clientID);
|
||||
|
||||
request.setUrl(_authURL);
|
||||
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkReply* requestReply = networkAccessManager.post(request, formData);
|
||||
connect(requestReply, &QNetworkReply::finished, this, &DomainAccountManager::requestAccessTokenFinished);
|
||||
}
|
||||
|
||||
void DomainAccountManager::requestAccessTokenFinished() {
|
||||
|
||||
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
|
||||
|
||||
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
|
||||
const QJsonObject& rootObject = jsonResponse.object();
|
||||
|
||||
auto httpStatus = requestReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (200 <= httpStatus && httpStatus < 300) {
|
||||
|
||||
// miniOrange plugin provides no scope.
|
||||
if (rootObject.contains("access_token")) {
|
||||
// Success.
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
_domain_name = nodeList->getDomainHandler().getHostname();
|
||||
QUrl rootURL = requestReply->url();
|
||||
rootURL.setPath("");
|
||||
setTokensFromJSON(rootObject, rootURL);
|
||||
emit loginComplete();
|
||||
} else {
|
||||
// Failure.
|
||||
qCDebug(networking) << "Received a response for password grant that is missing one or more expected values.";
|
||||
emit loginFailed();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Failure.
|
||||
qCDebug(networking) << "Error in response for password grant -" << httpStatus << requestReply->error()
|
||||
<< "-" << rootObject["error"].toString() << rootObject["error_description"].toString();
|
||||
emit loginFailed();
|
||||
}
|
||||
}
|
||||
|
||||
void DomainAccountManager::sendInterfaceAccessTokenToServer() {
|
||||
emit newTokens();
|
||||
}
|
||||
|
||||
bool DomainAccountManager::accessTokenIsExpired() {
|
||||
// ####### TODO: accessTokenIsExpired()
|
||||
return true;
|
||||
/*
|
||||
return domainAccessTokenExpiresIn.get() != -1 && domainAccessTokenExpiresIn.get() <= QDateTime::currentMSecsSinceEpoch();
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
bool DomainAccountManager::hasValidAccessToken() {
|
||||
// ###### TODO: wire this up to actually retrieve a token (based on session or storage) and confirm that it is in fact valid and relevant to the current domain.
|
||||
// QString currentDomainAccessToken = domainAccessToken.get();
|
||||
QString currentDomainAccessToken = _access_token;
|
||||
|
||||
// if (currentDomainAccessToken.isEmpty() || accessTokenIsExpired()) {
|
||||
if (currentDomainAccessToken.isEmpty()) {
|
||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||
qCDebug(networking) << "An access token is required for requests to"
|
||||
<< qPrintable(_authURL.toString());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ####### TODO
|
||||
|
||||
// if (!_isWaitingForTokenRefresh && needsToRefreshToken()) {
|
||||
// refreshAccessToken();
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DomainAccountManager::setTokensFromJSON(const QJsonObject& jsonObject, const QUrl& url) {
|
||||
_access_token = jsonObject["access_token"].toString();
|
||||
_refresh_token = jsonObject["refresh_token"].toString();
|
||||
|
||||
// ####### TODO: Enable and use these?
|
||||
// ####### TODO: Protect these per AccountManager?
|
||||
// ######: TODO: clientID needed?
|
||||
// qCDebug(networking) << "Storing a domain account with access-token for" << qPrintable(url.toString());
|
||||
// domainAccessToken.set(jsonObject["access_token"].toString());
|
||||
// domainAccessRefreshToken.set(jsonObject["refresh_token"].toString());
|
||||
// domainAccessTokenExpiresIn.set(QDateTime::currentMSecsSinceEpoch() + (jsonObject["expires_in"].toDouble() * 1000));
|
||||
// domainAccessTokenType.set(jsonObject["token_type"].toString());
|
||||
}
|
||||
|
||||
bool DomainAccountManager::checkAndSignalForAccessToken() {
|
||||
bool hasToken = hasValidAccessToken();
|
||||
|
||||
// ####### TODO: Handle hasToken == true.
|
||||
// It causes the login dialog not to display (OK) but somewhere the domain server needs to be sent it (and if domain server
|
||||
// gets error when trying to use it then user should be prompted to login).
|
||||
hasToken = false;
|
||||
|
||||
if (!hasToken) {
|
||||
// Emit a signal so somebody can call back to us and request an access token given a user name and password.
|
||||
|
||||
// Dialog can be hidden immediately after showing if we've just teleported to the domain, unless the signal is delayed.
|
||||
auto domain = _authURL.host();
|
||||
QTimer::singleShot(500, this, [this, domain] {
|
||||
emit this->authRequired(domain);
|
||||
});
|
||||
}
|
||||
|
||||
return hasToken;
|
||||
}
|
66
libraries/networking/src/DomainAccountManager.h
Normal file
66
libraries/networking/src/DomainAccountManager.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// DomainAccountManager.h
|
||||
// libraries/networking/src
|
||||
//
|
||||
// Created by David Rowe on 23 Jul 2020.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_DomainAccountManager_h
|
||||
#define hifi_DomainAccountManager_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
|
||||
class DomainAccountManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DomainAccountManager();
|
||||
|
||||
void setAuthURL(const QUrl& authURL);
|
||||
void setClientID(const QString& clientID) { _clientID = clientID; }
|
||||
|
||||
QString getUsername() { return _username; }
|
||||
QString getAccessToken() { return _access_token; }
|
||||
QString getRefreshToken() { return _refresh_token; }
|
||||
QString getAuthedDomain() { return _domain_name; }
|
||||
|
||||
bool isLoggedIn();
|
||||
|
||||
Q_INVOKABLE bool checkAndSignalForAccessToken();
|
||||
|
||||
public slots:
|
||||
void requestAccessToken(const QString& username, const QString& password);
|
||||
|
||||
void requestAccessTokenFinished();
|
||||
|
||||
signals:
|
||||
void authRequired(const QString& domain);
|
||||
void loginComplete();
|
||||
void loginFailed();
|
||||
void logoutComplete();
|
||||
void newTokens();
|
||||
|
||||
private slots:
|
||||
|
||||
private:
|
||||
bool hasValidAccessToken();
|
||||
bool accessTokenIsExpired();
|
||||
void setTokensFromJSON(const QJsonObject&, const QUrl& url);
|
||||
void sendInterfaceAccessTokenToServer();
|
||||
|
||||
QUrl _authURL;
|
||||
QString _clientID;
|
||||
QString _username; // ####### TODO: Store elsewhere?
|
||||
QString _access_token; // ####... ""
|
||||
QString _refresh_token; // ####... ""
|
||||
QString _domain_name; // ####... ""
|
||||
};
|
||||
|
||||
#endif // hifi_DomainAccountManager_h
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -24,6 +25,7 @@
|
|||
|
||||
#include "AddressManager.h"
|
||||
#include "Assignment.h"
|
||||
#include "DomainAccountManager.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "NodeList.h"
|
||||
#include "udt/Packet.h"
|
||||
|
@ -144,7 +146,8 @@ void DomainHandler::hardReset(QString reason) {
|
|||
bool DomainHandler::isHardRefusal(int reasonCode) {
|
||||
return (reasonCode == (int)ConnectionRefusedReason::ProtocolMismatch ||
|
||||
reasonCode == (int)ConnectionRefusedReason::TooManyUsers ||
|
||||
reasonCode == (int)ConnectionRefusedReason::NotAuthorized ||
|
||||
reasonCode == (int)ConnectionRefusedReason::NotAuthorizedMetaverse ||
|
||||
reasonCode == (int)ConnectionRefusedReason::NotAuthorizedDomain ||
|
||||
reasonCode == (int)ConnectionRefusedReason::TimedOut);
|
||||
}
|
||||
|
||||
|
@ -219,6 +222,8 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) {
|
|||
QString previousHost = _domainURL.host();
|
||||
_domainURL = domainURL;
|
||||
|
||||
_hasCheckedForDomainAccessToken = false;
|
||||
|
||||
if (previousHost != domainURL.host()) {
|
||||
qCDebug(networking) << "Updated domain hostname to" << domainURL.host();
|
||||
|
||||
|
@ -489,16 +494,33 @@ void DomainHandler::processICEResponsePacket(QSharedPointer<ReceivedMessage> mes
|
|||
}
|
||||
}
|
||||
|
||||
bool DomainHandler::reasonSuggestsLogin(ConnectionRefusedReason reasonCode) {
|
||||
bool DomainHandler::reasonSuggestsMetaverseLogin(ConnectionRefusedReason reasonCode) {
|
||||
switch (reasonCode) {
|
||||
case ConnectionRefusedReason::LoginError:
|
||||
case ConnectionRefusedReason::NotAuthorized:
|
||||
case ConnectionRefusedReason::LoginErrorMetaverse:
|
||||
case ConnectionRefusedReason::NotAuthorizedMetaverse:
|
||||
return true;
|
||||
|
||||
default:
|
||||
case ConnectionRefusedReason::Unknown:
|
||||
case ConnectionRefusedReason::ProtocolMismatch:
|
||||
case ConnectionRefusedReason::TooManyUsers:
|
||||
case ConnectionRefusedReason::NotAuthorizedDomain:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DomainHandler::reasonSuggestsDomainLogin(ConnectionRefusedReason reasonCode) {
|
||||
switch (reasonCode) {
|
||||
case ConnectionRefusedReason::LoginErrorDomain:
|
||||
case ConnectionRefusedReason::NotAuthorizedDomain:
|
||||
return true;
|
||||
|
||||
default:
|
||||
case ConnectionRefusedReason::Unknown:
|
||||
case ConnectionRefusedReason::ProtocolMismatch:
|
||||
case ConnectionRefusedReason::TooManyUsers:
|
||||
case ConnectionRefusedReason::NotAuthorizedMetaverse:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
@ -528,7 +550,9 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
|
|||
|
||||
// output to the log so the user knows they got a denied connection request
|
||||
// and check and signal for an access token so that we can make sure they are logged in
|
||||
qCWarning(networking) << "The domain-server denied a connection request: " << reasonMessage << " extraInfo:" << extraInfo;
|
||||
QString sanitizedExtraInfo = extraInfo.toLower().startsWith("http") ? "" : extraInfo; // Don't log URLs.
|
||||
qCWarning(networking) << "The domain-server denied a connection request: " << reasonMessage
|
||||
<< " extraInfo:" << sanitizedExtraInfo;
|
||||
|
||||
if (!_domainConnectionRefusals.contains(reasonMessage)) {
|
||||
_domainConnectionRefusals.insert(reasonMessage);
|
||||
|
@ -541,11 +565,12 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
|
|||
#endif
|
||||
}
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
||||
// Some connection refusal reasons imply that a login is required. If so, suggest a new login
|
||||
if (reasonSuggestsLogin(reasonCode)) {
|
||||
qCWarning(networking) << "Make sure you are logged in.";
|
||||
// Some connection refusal reasons imply that a login is required. If so, suggest a new login.
|
||||
if (reasonSuggestsMetaverseLogin(reasonCode)) {
|
||||
qCWarning(networking) << "Make sure you are logged in to the metaverse.";
|
||||
|
||||
auto accountManager = DependencyManager::get<AccountManager>();
|
||||
|
||||
if (!_hasCheckedForAccessToken) {
|
||||
accountManager->checkAndSignalForAccessToken();
|
||||
|
@ -559,6 +584,23 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
|
|||
accountManager->generateNewUserKeypair();
|
||||
_connectionDenialsSinceKeypairRegen = 0;
|
||||
}
|
||||
} else if (reasonSuggestsDomainLogin(reasonCode)) {
|
||||
qCWarning(networking) << "Make sure you are logged in to the domain.";
|
||||
|
||||
auto accountManager = DependencyManager::get<DomainAccountManager>();
|
||||
if (!extraInfo.isEmpty()) {
|
||||
auto extraInfoComponents = extraInfo.split("|");
|
||||
accountManager->setAuthURL(extraInfoComponents.value(0));
|
||||
accountManager->setClientID(extraInfoComponents.value(1));
|
||||
}
|
||||
|
||||
if (!_hasCheckedForDomainAccessToken) {
|
||||
accountManager->checkAndSignalForAccessToken();
|
||||
_hasCheckedForDomainAccessToken = true;
|
||||
}
|
||||
|
||||
// ####### TODO: regenerate key-pair after several failed connection attempts, similar to metaverse login code?
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 2/18/2014.
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -124,6 +125,7 @@ public:
|
|||
|
||||
bool isConnected() const { return _isConnected; }
|
||||
void setIsConnected(bool isConnected);
|
||||
|
||||
bool isServerless() const { return _domainURL.scheme() != URL_SCHEME_HIFI; }
|
||||
bool getInterstitialModeEnabled() const;
|
||||
void setInterstitialModeEnabled(bool enableInterstitialMode);
|
||||
|
@ -172,14 +174,14 @@ public:
|
|||
* <td>The communications protocols of the domain and your Interface are not the same.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><strong>LoginError</strong></td>
|
||||
* <td><strong>LoginErrorMetaverse</strong></td>
|
||||
* <td><code>2</code></td>
|
||||
* <td>You could not be logged into the domain.</td>
|
||||
* <td>You could not be logged into the domain per your metaverse login.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><strong>NotAuthorized</strong></td>
|
||||
* <td><strong>NotAuthorizedMetaverse</strong></td>
|
||||
* <td><code>3</code></td>
|
||||
* <td>You are not authorized to connect to the domain.</td>
|
||||
* <td>You are not authorized to connect to the domain per your metaverse login.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><strong>TooManyUsers</strong></td>
|
||||
|
@ -191,6 +193,16 @@ public:
|
|||
* <td><code>5</code></td>
|
||||
* <td>Connecting to the domain timed out.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><strong>LoginErrorDomain</strong></td>
|
||||
* <td><code>2</code></td>
|
||||
* <td>You could not be logged into the domain per your domain login.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><strong>NotAuthorizedDomain</strong></td>
|
||||
* <td><code>6</code></td>
|
||||
* <td>You are not authorized to connect to the domain per your domain login.</td>
|
||||
* </tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
* @typedef {number} Window.ConnectionRefusedReason
|
||||
|
@ -198,10 +210,12 @@ public:
|
|||
enum class ConnectionRefusedReason : uint8_t {
|
||||
Unknown,
|
||||
ProtocolMismatch,
|
||||
LoginError,
|
||||
NotAuthorized,
|
||||
LoginErrorMetaverse,
|
||||
NotAuthorizedMetaverse,
|
||||
TooManyUsers,
|
||||
TimedOut
|
||||
TimedOut,
|
||||
LoginErrorDomain,
|
||||
NotAuthorizedDomain
|
||||
};
|
||||
|
||||
public slots:
|
||||
|
@ -247,7 +261,8 @@ signals:
|
|||
void limitOfSilentDomainCheckInsReached();
|
||||
|
||||
private:
|
||||
bool reasonSuggestsLogin(ConnectionRefusedReason reasonCode);
|
||||
bool reasonSuggestsMetaverseLogin(ConnectionRefusedReason reasonCode);
|
||||
bool reasonSuggestsDomainLogin(ConnectionRefusedReason reasonCode);
|
||||
void sendDisconnectPacket();
|
||||
void hardReset(QString reason);
|
||||
|
||||
|
@ -278,6 +293,7 @@ private:
|
|||
|
||||
QSet<QString> _domainConnectionRefusals;
|
||||
bool _hasCheckedForAccessToken { false };
|
||||
bool _hasCheckedForDomainAccessToken { false };
|
||||
int _connectionDenialsSinceKeypairRegen { 0 };
|
||||
int _checkInPacketsSinceLastReply { 0 };
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "NetworkAccessManager.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include "NetworkingConstants.h"
|
||||
|
||||
HTTPResourceRequest::~HTTPResourceRequest() {
|
||||
if (_reply) {
|
||||
|
@ -54,7 +55,7 @@ void HTTPResourceRequest::doSend() {
|
|||
|
||||
QNetworkRequest networkRequest(_url);
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
|
||||
if (_cacheEnabled) {
|
||||
networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
|
|
|
@ -30,6 +30,14 @@ namespace NetworkingConstants {
|
|||
|
||||
// Web Engine requests to this parent domain have an account authorization header added
|
||||
const QString AUTH_HOSTNAME_BASE = "highfidelity.com";
|
||||
const QStringList IS_AUTHABLE_HOSTNAME = { "highfidelity.com", "highfidelity.io" };
|
||||
|
||||
// Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers.
|
||||
const QByteArray VIRCADIA_USER_AGENT = "Mozilla/5.0 (VircadiaInterface)";
|
||||
|
||||
const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (VircadiaInterface)";
|
||||
const QString METAVERSE_USER_AGENT = "Chrome/48.0 (VircadiaInterface)";
|
||||
const QString MOBILE_USER_AGENT = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
|
||||
|
||||
const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml");
|
||||
const QUrl MASTER_BUILDS_XML_URL("https://highfidelity.com/dev-builds.xml");
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 2/15/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -33,6 +34,7 @@
|
|||
#include "AddressManager.h"
|
||||
#include "Assignment.h"
|
||||
#include "AudioHelpers.h"
|
||||
#include "DomainAccountManager.h"
|
||||
#include "HifiSockAddr.h"
|
||||
#include "FingerprintUtils.h"
|
||||
|
||||
|
@ -103,6 +105,13 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
|
|||
// clear our NodeList when logout is requested
|
||||
connect(accountManager.data(), &AccountManager::logoutComplete , this, [this]{ reset("Logged out"); });
|
||||
|
||||
// Only used in Interface.
|
||||
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
|
||||
if (domainAccountManager) {
|
||||
_hasDomainAccountManager = true;
|
||||
connect(domainAccountManager.data(), &DomainAccountManager::newTokens, this, &NodeList::sendDomainServerCheckIn);
|
||||
}
|
||||
|
||||
// anytime we get a new node we will want to attempt to punch to it
|
||||
connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch);
|
||||
connect(this, &LimitedNodeList::nodeSocketUpdated, this, &NodeList::startNodeHolePunch);
|
||||
|
@ -379,6 +388,7 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
if (domainPacketType == PacketType::DomainConnectRequest) {
|
||||
|
||||
#if (PR_BUILD || DEV_BUILD)
|
||||
// #######
|
||||
if (_shouldSendNewerVersion) {
|
||||
domainPacket->setVersion(versionForPacketType(domainPacketType) + 1);
|
||||
}
|
||||
|
@ -466,6 +476,7 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
packetStream << _ownerType.load() << publicSockAddr << localSockAddr << _nodeTypesOfInterest.toList();
|
||||
packetStream << DependencyManager::get<AddressManager>()->getPlaceName();
|
||||
|
||||
// ####### TODO: Also send if need to send new domainLogin data?
|
||||
if (!domainIsConnected) {
|
||||
DataServerAccountInfo& accountInfo = accountManager->getAccountInfo();
|
||||
packetStream << accountInfo.getUsername();
|
||||
|
@ -474,6 +485,23 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
if (requiresUsernameSignature && accountManager->getAccountInfo().hasPrivateKey()) {
|
||||
const QByteArray& usernameSignature = accountManager->getAccountInfo().getUsernameSignature(connectionToken);
|
||||
packetStream << usernameSignature;
|
||||
} else {
|
||||
// ####### TODO: Only append if are going to send domain username?
|
||||
packetStream << QString(""); // Placeholder in case have domain username.
|
||||
}
|
||||
} else {
|
||||
// ####### TODO: Only append if are going to send domainUsername?
|
||||
packetStream << QString("") << QString(""); // Placeholders in case have domain username.
|
||||
}
|
||||
|
||||
// Send domain domain login data from Interface to domain server.
|
||||
if (_hasDomainAccountManager) {
|
||||
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
|
||||
if (!domainAccountManager->getUsername().isEmpty()) {
|
||||
packetStream << domainAccountManager->getUsername();
|
||||
if (!domainAccountManager->getAccessToken().isEmpty()) {
|
||||
packetStream << (domainAccountManager->getAccessToken() + ":" + domainAccountManager->getRefreshToken());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -196,6 +196,8 @@ private:
|
|||
#if (PR_BUILD || DEV_BUILD)
|
||||
bool _shouldSendNewerVersion { false };
|
||||
#endif
|
||||
|
||||
bool _hasDomainAccountManager { false };
|
||||
};
|
||||
|
||||
#endif // hifi_NodeList_h
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Seth Alves on 2016-6-1.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -51,6 +52,9 @@ public:
|
|||
void setVerifiedUserName(QString userName) { _verifiedUserName = userName.toLower(); }
|
||||
const QString& getVerifiedUserName() const { return _verifiedUserName; }
|
||||
|
||||
void setVerifiedDomainUserName(QString userName) { _verifiedDomainUserName = userName.toLower(); }
|
||||
const QString& getVerifiedDomainUserName() const { return _verifiedDomainUserName; }
|
||||
|
||||
void setGroupID(QUuid groupID) { _groupID = groupID; if (!groupID.isNull()) { _groupIDSet = true; }}
|
||||
QUuid getGroupID() const { return _groupID; }
|
||||
bool isGroup() const { return _groupIDSet; }
|
||||
|
@ -99,6 +103,7 @@ protected:
|
|||
QString _id;
|
||||
QUuid _rankID { QUuid() }; // 0 unless this is for a group
|
||||
QString _verifiedUserName;
|
||||
QString _verifiedDomainUserName;
|
||||
|
||||
bool _groupIDSet { false };
|
||||
QUuid _groupID;
|
||||
|
|
|
@ -39,7 +39,7 @@ QNetworkReply* OAuthNetworkAccessManager::createRequest(QNetworkAccessManager::O
|
|||
&& req.url().host() == MetaverseAPI::getCurrentMetaverseServerURL().host()) {
|
||||
QNetworkRequest authenticatedRequest(req);
|
||||
authenticatedRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
authenticatedRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
authenticatedRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
authenticatedRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER,
|
||||
accountManager->getAccountInfo().getAccessToken().authorizationHeaderValue());
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "HTTPResourceRequest.h"
|
||||
#include "NetworkAccessManager.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include "NetworkingConstants.h"
|
||||
|
||||
ResourceManager::ResourceManager(bool atpSupportEnabled) : _atpSupportEnabled(atpSupportEnabled) {
|
||||
_thread.setObjectName("Resource Manager Thread");
|
||||
|
@ -157,7 +158,7 @@ bool ResourceManager::resourceExists(const QUrl& url) {
|
|||
QNetworkRequest request{ url };
|
||||
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
|
||||
auto reply = networkAccessManager.head(request);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "NetworkAccessManager.h"
|
||||
#include "NetworkLogging.h"
|
||||
#include "NetworkingConstants.h"
|
||||
|
||||
namespace SandboxUtils {
|
||||
|
||||
|
@ -29,7 +30,7 @@ QNetworkReply* getStatus() {
|
|||
auto& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest sandboxStatus(SANDBOX_STATUS_URL);
|
||||
sandboxStatus.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
sandboxStatus.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
return networkAccessManager.get(sandboxStatus);
|
||||
}
|
||||
|
||||
|
|
|
@ -543,12 +543,8 @@ void CharacterController::setLocalBoundingBox(const glm::vec3& minCorner, const
|
|||
_minStepHeight = DEFAULT_MIN_STEP_HEIGHT_FACTOR * (_halfHeight + _radius);
|
||||
_maxStepHeight = DEFAULT_MAX_STEP_HEIGHT_FACTOR * (_halfHeight + _radius);
|
||||
|
||||
if (_physicsEngine) {
|
||||
// must REMOVE from world prior to shape update
|
||||
_pendingFlags |= PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION;
|
||||
}
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_SHAPE;
|
||||
_pendingFlags |= PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_ADD_DETAILED_TO_SIMULATION;
|
||||
_pendingFlags |= PENDING_FLAG_UPDATE_SHAPE | PENDING_FLAG_REMOVE_FROM_SIMULATION | PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION |
|
||||
PENDING_FLAG_ADD_TO_SIMULATION | PENDING_FLAG_ADD_DETAILED_TO_SIMULATION;
|
||||
}
|
||||
|
||||
// it's ok to change offset immediately -- there are no thread safety issues here
|
||||
|
|
|
@ -226,6 +226,7 @@ void Model::updateRenderItems() {
|
|||
modelTransform.setScale(glm::vec3(1.0f));
|
||||
|
||||
PrimitiveMode primitiveMode = self->getPrimitiveMode();
|
||||
auto renderWithZones = self->getRenderWithZones();
|
||||
auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags();
|
||||
bool cauterized = self->isCauterized();
|
||||
|
||||
|
@ -241,7 +242,8 @@ void Model::updateRenderItems() {
|
|||
bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();
|
||||
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, meshState, useDualQuaternionSkinning,
|
||||
invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) {
|
||||
invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags,
|
||||
cauterized, renderWithZones](ModelMeshPartPayload& data) {
|
||||
if (useDualQuaternionSkinning) {
|
||||
data.updateClusterBuffer(meshState.clusterDualQuaternions);
|
||||
data.computeAdjustedLocalBound(meshState.clusterDualQuaternions);
|
||||
|
@ -268,6 +270,7 @@ void Model::updateRenderItems() {
|
|||
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
|
||||
|
||||
data.setCauterized(cauterized);
|
||||
data.setRenderWithZones(renderWithZones);
|
||||
data.updateKey(renderItemKeyGlobalFlags);
|
||||
data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning);
|
||||
});
|
||||
|
@ -282,11 +285,6 @@ void Model::setRenderItemsNeedUpdate() {
|
|||
emit requestRenderUpdate();
|
||||
}
|
||||
|
||||
void Model::setPrimitiveMode(PrimitiveMode primitiveMode) {
|
||||
_primitiveMode = primitiveMode;
|
||||
setRenderItemsNeedUpdate();
|
||||
}
|
||||
|
||||
void Model::reset() {
|
||||
if (isLoaded()) {
|
||||
const HFMModel& hfmModel = getHFMModel();
|
||||
|
@ -960,6 +958,13 @@ void Model::setCauterized(bool cauterized, const render::ScenePointer& scene) {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::setPrimitiveMode(PrimitiveMode primitiveMode) {
|
||||
if (_primitiveMode != primitiveMode) {
|
||||
_primitiveMode = primitiveMode;
|
||||
setRenderItemsNeedUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void Model::setCullWithParent(bool cullWithParent) {
|
||||
if (_cullWithParent != cullWithParent) {
|
||||
_cullWithParent = cullWithParent;
|
||||
|
@ -977,13 +982,10 @@ void Model::setCullWithParent(bool cullWithParent) {
|
|||
}
|
||||
|
||||
void Model::setRenderWithZones(const QVector<QUuid>& renderWithZones) {
|
||||
render::Transaction transaction;
|
||||
for (auto item : _modelMeshRenderItemIDs) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [renderWithZones](ModelMeshPartPayload& data) {
|
||||
data.setRenderWithZones(renderWithZones);
|
||||
});
|
||||
if (_renderWithZones != renderWithZones) {
|
||||
_renderWithZones = renderWithZones;
|
||||
setRenderItemsNeedUpdate();
|
||||
}
|
||||
AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
const render::ItemKey Model::getRenderItemKeyGlobalFlags() const {
|
||||
|
|
|
@ -121,6 +121,7 @@ public:
|
|||
void setCullWithParent(bool value);
|
||||
|
||||
void setRenderWithZones(const QVector<QUuid>& renderWithZones);
|
||||
const QVector<QUuid>& getRenderWithZones() const { return _renderWithZones; }
|
||||
|
||||
// Access the current RenderItemKey Global Flags used by the model and applied to the render items representing the parts of the model.
|
||||
const render::ItemKey getRenderItemKeyGlobalFlags() const;
|
||||
|
@ -499,6 +500,7 @@ protected:
|
|||
render::ItemKey _renderItemKeyGlobalFlags;
|
||||
bool _cauterized { false };
|
||||
bool _cullWithParent { false };
|
||||
QVector<QUuid> _renderWithZones;
|
||||
|
||||
bool shouldInvalidatePayloadShapeKey(int meshIndex);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "../StencilMaskPass.h"
|
||||
|
||||
#include "NetworkAccessManager.h"
|
||||
#include "NetworkingConstants.h"
|
||||
|
||||
static std::mutex fontMutex;
|
||||
|
||||
|
@ -97,7 +98,7 @@ Font::Pointer Font::load(const QString& family) {
|
|||
|
||||
QNetworkRequest networkRequest;
|
||||
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
networkRequest.setUrl(family);
|
||||
|
||||
auto networkReply = networkAccessManager.get(networkRequest);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QDirIterator>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "ScriptEngine.h"
|
||||
|
@ -191,7 +192,7 @@ void ScriptsModel::requestDefaultFiles(QString marker) {
|
|||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(downloadFinished()));
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ void XMLHttpRequestClass::abort() {
|
|||
}
|
||||
|
||||
void XMLHttpRequestClass::setRequestHeader(const QString& name, const QString& value) {
|
||||
_request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
_request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
|
||||
_request.setRawHeader(QByteArray(name.toLatin1()), QByteArray(value.toLatin1()));
|
||||
}
|
||||
|
||||
|
|
|
@ -81,9 +81,6 @@ const int BYTES_PER_FLAGS = 1;
|
|||
typedef unsigned char colorPart;
|
||||
typedef unsigned char nodeColor[BYTES_PER_COLOR + BYTES_PER_FLAGS];
|
||||
|
||||
// Use a custom User-Agent to avoid ModSecurity filtering, e.g. by hosting providers.
|
||||
const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)";
|
||||
|
||||
// Equivalent to time_t but in usecs instead of secs
|
||||
quint64 usecTimestampNow(bool wantDebug = false);
|
||||
void usecTimestampNowForceClockSkew(qint64 clockSkew);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QtQml/QQmlContext>
|
||||
|
||||
#include "RequestFilters.h"
|
||||
#include "NetworkingConstants.h"
|
||||
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
|
||||
|
@ -26,8 +27,7 @@ static std::mutex FileTypeProfile_mutex;
|
|||
FileTypeProfile::FileTypeProfile(QQmlContext* parent) :
|
||||
ContextAwareProfile(parent)
|
||||
{
|
||||
static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";
|
||||
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
|
||||
setHttpUserAgent(NetworkingConstants::WEB_ENGINE_USER_AGENT);
|
||||
|
||||
setStorageName(QML_WEB_ENGINE_STORAGE_NAME);
|
||||
setOffTheRecord(false);
|
||||
|
|
|
@ -27,10 +27,10 @@ namespace {
|
|||
|
||||
bool isAuthableHighFidelityURL(const QUrl& url) {
|
||||
auto metaverseServerURL = MetaverseAPI::getCurrentMetaverseServerURL();
|
||||
static const QStringList HF_HOSTS = {
|
||||
"highfidelity.com", "highfidelity.io",
|
||||
metaverseServerURL.toString(),
|
||||
static QStringList HF_HOSTS = {
|
||||
metaverseServerURL.toString()
|
||||
};
|
||||
HF_HOSTS << NetworkingConstants::IS_AUTHABLE_HOSTNAME;
|
||||
const auto& scheme = url.scheme();
|
||||
const auto& host = url.host();
|
||||
|
||||
|
@ -83,9 +83,9 @@ void RequestFilters::interceptHFWebEngineRequest(QWebEngineUrlRequestInfo& info,
|
|||
}
|
||||
}
|
||||
static const QString USER_AGENT = "User-Agent";
|
||||
const QString tokenStringMobile{ "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36" };
|
||||
const QString tokenStringMetaverse{ "Chrome/48.0 (HighFidelityInterface)" };
|
||||
const QString tokenStringLimitedCommerce{ "Chrome/48.0 (HighFidelityInterface limitedCommerce)" };
|
||||
const QString tokenStringMobile{ NetworkingConstants::MOBILE_USER_AGENT };
|
||||
const QString tokenStringMetaverse{ NetworkingConstants::METAVERSE_USER_AGENT };
|
||||
const QString tokenStringLimitedCommerce{ "Chrome/48.0 (VircadiaInterface limitedCommerce)" };
|
||||
|
||||
const QString tokenString = !isAuthable ? tokenStringMobile : (accountManager->getLimitedCommerce() ? tokenStringLimitedCommerce : tokenStringMetaverse);
|
||||
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
|
||||
|
|
|
@ -26,8 +26,8 @@ if (osType == "Darwin") {
|
|||
options["app-bundle-id"] = "com.highfidelity.hifi-screenshare";
|
||||
} else if (osType == "Windows_NT") {
|
||||
options["version-string"] = {
|
||||
CompanyName: "High Fidelity, Inc.",
|
||||
FileDescription: "High Fidelity Screenshare",
|
||||
CompanyName: "Vircadia",
|
||||
FileDescription: "Vircadia Screenshare",
|
||||
ProductName: NAME,
|
||||
OriginalFilename: NAME + ".exe"
|
||||
}
|
||||
|
@ -47,4 +47,3 @@ packager(options)
|
|||
console.error("There was an error writing the packaged console: " + error.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -389,7 +389,8 @@
|
|||
|
||||
function time() {
|
||||
var d = new Date();
|
||||
var month = (d.getMonth()).toString();
|
||||
// Months are returned in range 0-11 instead of 1-12, so we have to add 1.
|
||||
var month = (d.getMonth() + 1).toString();
|
||||
var day = (d.getDate()).toString();
|
||||
var h = (d.getHours()).toString();
|
||||
var m = (d.getMinutes()).toString();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
// Created by Fluffy Jenkins January 2020.
|
||||
// Copyright 2020 Fluffy Jenkins
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// For any future coders, please keep me in the loop when making changes.
|
||||
// Please tag me in any Pull Requests.
|
||||
|
@ -98,6 +99,8 @@ function init() {
|
|||
chatBar.sendToQml(JSON.stringify({visible: false, history: chatBarHistory}));
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Messages.messageReceived.connect(messageReceived);
|
||||
AvatarManager.avatarAddedEvent.connect(avatarJoinsDomain);
|
||||
AvatarManager.avatarRemovedEvent.connect(avatarLeavesDomain);
|
||||
|
||||
connectWebSocket();
|
||||
}
|
||||
|
@ -510,13 +513,17 @@ function messageReceived(channel, message) {
|
|||
}));
|
||||
}
|
||||
}
|
||||
if (cmd.type === "ShowChatWindow") {
|
||||
toggleMainChatWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function time() {
|
||||
var d = new Date();
|
||||
var month = (d.getMonth()).toString();
|
||||
// Months are returned in range 0-11 instead of 1-12, so we have to add 1.
|
||||
var month = (d.getMonth() + 1).toString();
|
||||
var day = (d.getDate()).toString();
|
||||
var h = (d.getHours()).toString();
|
||||
var m = (d.getMinutes()).toString();
|
||||
|
@ -618,6 +625,46 @@ function setVisible(_visible) {
|
|||
visible = _visible;
|
||||
}
|
||||
|
||||
function avatarJoinsDomain(sessionID) {
|
||||
Script.setTimeout(function () {
|
||||
var messageText = AvatarManager.getPalData([sessionID]).data[0].sessionDisplayName + " has joined."
|
||||
var messageColor = { red: 122, green: 122, blue: 122 };
|
||||
|
||||
addToLog(messageText, "Notice", messageColor, "Domain");
|
||||
|
||||
if (!mutedAudio["Domain"]) {
|
||||
playNotificationSound();
|
||||
}
|
||||
|
||||
if (!muted["Domain"]) {
|
||||
Messages.sendLocalMessage(FLOOF_NOTIFICATION_CHANNEL, JSON.stringify({
|
||||
sender: "(D)",
|
||||
text: messageText,
|
||||
colour: { text: messageColor }
|
||||
}));
|
||||
}
|
||||
}, 500); // Wait 500ms for the avatar to load to properly get info about them.
|
||||
}
|
||||
|
||||
function avatarLeavesDomain(sessionID) {
|
||||
var messageText = AvatarManager.getPalData([sessionID]).data[0].sessionDisplayName + " has left."
|
||||
var messageColor = { red: 122, green: 122, blue: 122 };
|
||||
|
||||
addToLog(messageText, "Notice", messageColor, "Domain");
|
||||
|
||||
if (!mutedAudio["Domain"]) {
|
||||
playNotificationSound();
|
||||
}
|
||||
|
||||
if (!muted["Domain"]) {
|
||||
Messages.sendLocalMessage(FLOOF_NOTIFICATION_CHANNEL, JSON.stringify({
|
||||
sender: "(D)",
|
||||
text: messageText,
|
||||
colour: { text: messageColor }
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
if (event.key === H_KEY && !event.isAutoRepeat && event.isControl) {
|
||||
toggleMainChatWindow()
|
||||
|
@ -636,11 +683,25 @@ function shutdown() {
|
|||
} catch (e) {
|
||||
// empty
|
||||
}
|
||||
|
||||
try {
|
||||
AvatarManager.avatarAddedEvent.disconnect(avatarJoinsDomain);
|
||||
} catch (e) {
|
||||
// empty
|
||||
}
|
||||
|
||||
try {
|
||||
AvatarManager.avatarRemovedEvent.disconnect(avatarLeavesDomain);
|
||||
} catch (e) {
|
||||
// empty
|
||||
}
|
||||
|
||||
try {
|
||||
Controller.keyPressEvent.disconnect(keyPressEvent);
|
||||
} catch (e) {
|
||||
// empty
|
||||
}
|
||||
|
||||
chatBar.close();
|
||||
chatHistory.close();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
//
|
||||
// notificationCore.js
|
||||
//
|
||||
// Created by Fluffy Jenkins January 2020.
|
||||
// Copyright 2020 Fluffy Jenkins
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// For any future coders, please keep me in the loop when making changes.
|
||||
// Please tag me in any Pull Requests.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
"use strict";
|
||||
var notificationList = [];
|
||||
|
||||
|
@ -6,6 +20,7 @@ var sizeData = {30: {widthMul: 1.8, heightMul: 2.05, split: 35, size: 30}};
|
|||
var DEFAULT_SIZE = 30;
|
||||
var DEFAULT_OFFSET = 10;
|
||||
var FLOOF_NOTIFICATION_CHANNEL = "Floof-Notif";
|
||||
var MAIN_CHAT_APP_CHANNEL = "Chat";
|
||||
|
||||
var offset = DEFAULT_OFFSET;
|
||||
|
||||
|
@ -161,4 +176,19 @@ function notif(text, colour) {
|
|||
notificationList.push(noti);
|
||||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanUp);
|
||||
Controller.mousePressEvent.connect(function (event) {
|
||||
// Overlays.getOverlayAtPoint applies only to 2D overlays.
|
||||
var overlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||
if (overlay) {
|
||||
for (var i = 0; i < notificationList.length; i++) {
|
||||
if (overlay === notificationList[i].id) {
|
||||
Overlays.deleteOverlay(notificationList[i].id)
|
||||
Messages.sendMessage(MAIN_CHAT_APP_CHANNEL, JSON.stringify({
|
||||
type: "ShowChatWindow",
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Script.scriptEnding.connect(cleanUp);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// examples
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -22,6 +23,7 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/snapshot.js",
|
||||
"system/pal.js", // "system/mod.js", // older UX, if you prefer
|
||||
"system/avatarapp.js",
|
||||
"system/graphicsSettings.js",
|
||||
"system/makeUserConnection.js",
|
||||
"system/marketplaces/marketplaces.js",
|
||||
"system/notifications.js",
|
||||
|
|
|
@ -44,7 +44,8 @@ var CREATE_TOOLS_WIDTH = 490;
|
|||
var MAX_DEFAULT_ENTITY_LIST_HEIGHT = 942;
|
||||
var ENTIRE_DOMAIN_SCAN_RADIUS = 27713;
|
||||
|
||||
var DEFAULT_IMAGE = "https://hifi-content.s3.amazonaws.com/DomainContent/production/no-image.jpg";
|
||||
var DEFAULT_IMAGE = "http://eu-central-1.linodeobjects.com/vircadia-assets/interface/default/default_image.jpg";
|
||||
var DEFAULT_PARTICLE = "http://eu-central-1.linodeobjects.com/vircadia-assets/interface/default/default_particle.png";
|
||||
|
||||
var createToolsWindow = new CreateWindow(
|
||||
Script.resolvePath("qml/EditTools.qml"),
|
||||
|
@ -436,7 +437,7 @@ const DEFAULT_ENTITY_PROPERTIES = {
|
|||
ParticleEffect: {
|
||||
lifespan: 1.5,
|
||||
maxParticles: 10,
|
||||
textures: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png",
|
||||
textures: DEFAULT_PARTICLE,
|
||||
emitRate: 5.5,
|
||||
emitSpeed: 0,
|
||||
speedSpread: 0,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Modified by Daniela Fontes * @DanielaFifo and Tiago Andrade @TagoWill on 4/7/2017
|
||||
// Modified by David Back on 1/9/2018
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Copyright 2020 Vircadia contributors
|
||||
//
|
||||
// This script implements a class useful for building tools for editing entities.
|
||||
//
|
||||
|
@ -31,6 +32,46 @@ function deepCopy(v) {
|
|||
return JSON.parse(JSON.stringify(v));
|
||||
}
|
||||
|
||||
function getDuplicateAppendedName(name) {
|
||||
var appendiceChar = "(0123456789)";
|
||||
var currentChar = "";
|
||||
var rippedName = name;
|
||||
var existingSequence = 0;
|
||||
var sequenceReader = "";
|
||||
for (var i = name.length - 1; i >= 0; i--) {
|
||||
currentChar = name.charAt(i);
|
||||
if (i === (name.length - 1) && currentChar !== ")") {
|
||||
rippedName = name;
|
||||
break;
|
||||
} else {
|
||||
if (appendiceChar.indexOf(currentChar) === -1) {
|
||||
rippedName = name;
|
||||
break;
|
||||
} else {
|
||||
if (currentChar == "(" && i === name.length - 2) {
|
||||
rippedName = name;
|
||||
break;
|
||||
} else {
|
||||
if (currentChar == "(") {
|
||||
rippedName = name.substr(0, i-1);
|
||||
existingSequence = parseInt(sequenceReader);
|
||||
break;
|
||||
} else {
|
||||
sequenceReader = currentChar + sequenceReader;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (existingSequence === 0) {
|
||||
rippedName = rippedName.trim() + " (2)";
|
||||
} else {
|
||||
existingSequence++;
|
||||
rippedName = rippedName.trim() + " (" + existingSequence + ")";
|
||||
}
|
||||
return rippedName.trim();
|
||||
}
|
||||
|
||||
SelectionManager = (function() {
|
||||
var that = {};
|
||||
|
||||
|
@ -289,6 +330,8 @@ SelectionManager = (function() {
|
|||
properties.localRotation = properties.rotation;
|
||||
}
|
||||
|
||||
properties.name = getDuplicateAppendedName(properties.name);
|
||||
|
||||
properties.localVelocity = Vec3.ZERO;
|
||||
properties.localAngularVelocity = Vec3.ZERO;
|
||||
|
||||
|
|
70
scripts/system/graphicsSettings.js
Normal file
70
scripts/system/graphicsSettings.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// graphicsSettings.js
|
||||
//
|
||||
// Created by Kalila L. on 8/5/2020
|
||||
// Copyright 2020 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
var AppUi = Script.require('appUi');
|
||||
|
||||
// cellphone-cog MDI
|
||||
// var customIcon = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24"><path fill="white" d="M9.82,12.5C9.84,12.33 9.86,12.17 9.86,12C9.86,11.83 9.84,11.67 9.82,11.5L10.9,10.69C11,10.62 11,10.5 10.96,10.37L9.93,8.64C9.87,8.53 9.73,8.5 9.62,8.53L8.34,9.03C8.07,8.83 7.78,8.67 7.47,8.54L7.27,7.21C7.27,7.09 7.16,7 7.03,7H5C4.85,7 4.74,7.09 4.72,7.21L4.5,8.53C4.21,8.65 3.92,8.83 3.65,9L2.37,8.5C2.25,8.47 2.12,8.5 2.06,8.63L1.03,10.36C0.97,10.5 1,10.61 1.1,10.69L2.18,11.5C2.16,11.67 2.15,11.84 2.15,12C2.15,12.17 2.17,12.33 2.19,12.5L1.1,13.32C1,13.39 1,13.53 1.04,13.64L2.07,15.37C2.13,15.5 2.27,15.5 2.38,15.5L3.66,15C3.93,15.18 4.22,15.34 4.53,15.47L4.73,16.79C4.74,16.91 4.85,17 5,17H7.04C7.17,17 7.28,16.91 7.29,16.79L7.5,15.47C7.8,15.35 8.09,15.17 8.36,15L9.64,15.5C9.76,15.53 9.89,15.5 9.95,15.37L11,13.64C11.04,13.53 11,13.4 10.92,13.32L9.82,12.5M6,13.75C5,13.75 4.2,12.97 4.2,12C4.2,11.03 5,10.25 6,10.25C7,10.25 7.8,11.03 7.8,12C7.8,12.97 7,13.75 6,13.75M17,1H7A2,2 0 0,0 5,3V6H7V4H17V20H7V18H5V21A2,2 0 0,0 7,23H17A2,2 0 0,0 19,21V3A2,2 0 0,0 17,1Z" /></svg>'
|
||||
|
||||
// application-cog MDI
|
||||
var customIcon = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24"><path fill="white" d="M21.7 18.6V17.6L22.8 16.8C22.9 16.7 23 16.6 22.9 16.5L21.9 14.8C21.9 14.7 21.7 14.7 21.6 14.7L20.4 15.2C20.1 15 19.8 14.8 19.5 14.7L19.3 13.4C19.3 13.3 19.2 13.2 19.1 13.2H17.1C16.9 13.2 16.8 13.3 16.8 13.4L16.6 14.7C16.3 14.9 16.1 15 15.8 15.2L14.6 14.7C14.5 14.7 14.4 14.7 14.3 14.8L13.3 16.5C13.3 16.6 13.3 16.7 13.4 16.8L14.5 17.6V18.6L13.4 19.4C13.3 19.5 13.2 19.6 13.3 19.7L14.3 21.4C14.4 21.5 14.5 21.5 14.6 21.5L15.8 21C16 21.2 16.3 21.4 16.6 21.5L16.8 22.8C16.9 22.9 17 23 17.1 23H19.1C19.2 23 19.3 22.9 19.3 22.8L19.5 21.5C19.8 21.3 20 21.2 20.3 21L21.5 21.4C21.6 21.4 21.7 21.4 21.8 21.3L22.8 19.6C22.9 19.5 22.9 19.4 22.8 19.4L21.7 18.6M18 19.5C17.2 19.5 16.5 18.8 16.5 18S17.2 16.5 18 16.5 19.5 17.2 19.5 18 18.8 19.5 18 19.5M11.29 20H5C3.89 20 3 19.1 3 18V6C3 4.89 3.9 4 5 4H19C20.11 4 21 4.9 21 6V11.68C20.38 11.39 19.71 11.18 19 11.08V8H5V18H11C11 18.7 11.11 19.37 11.29 20Z" /></svg>'
|
||||
var lqIcon = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24"><path fill="white" d="M14.5,13.5H16.5V10.5H14.5M18,14C18,14.6 17.6,15 17,15H16.25V16.5H14.75V15H14C13.4,15 13,14.6 13,14V10C13,9.4 13.4,9 14,9H17C17.6,9 18,9.4 18,10M19,4H5A2,2 0 0,0 3,6V18A2,2 0 0,0 5,20H19A2,2 0 0,0 21,18V6A2,2 0 0,0 19,4M11,13.5V15H6V9H7.5V13.5H11Z" /></svg>';
|
||||
var mqIcon = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24"><path fill="white" d="M21,6V18A2,2 0 0,1 19,20H5A2,2 0 0,1 3,18V6A2,2 0 0,1 5,4H19A2,2 0 0,1 21,6M12,10C12,9.5 11.5,9 11,9H6.5C6,9 5.5,9.5 5.5,10V15H7V10.5H8V14H9.5V10.5H10.5V15H12V10M14.5,9A1,1 0 0,0 13.5,10V14A1,1 0 0,0 14.5,15H15.5V16.5H16.75V15H17.5A1,1 0 0,0 18.5,14V10A1,1 0 0,0 17.5,9H14.5M15,10.5H17V13.5H15V10.5Z" /></svg>';
|
||||
var hqIcon = 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24"><path fill="white" d="M14.5,13.5H16.5V10.5H14.5M18,14A1,1 0 0,1 17,15H16.25V16.5H14.75V15H14A1,1 0 0,1 13,14V10A1,1 0 0,1 14,9H17A1,1 0 0,1 18,10M11,15H9.5V13H7.5V15H6V9H7.5V11.5H9.5V9H11M19,4H5C3.89,4 3,4.89 3,6V18A2,2 0 0,0 5,20H19A2,2 0 0,0 21,18V6C21,4.89 20.1,4 19,4Z" /></svg>';
|
||||
|
||||
var BUTTON_NAME = "GRAPHICS";
|
||||
var GRAPHICS_QML_SOURCE = "hifi/dialogs/graphics/GraphicsSettings.qml";
|
||||
var ui;
|
||||
|
||||
function getIcon() {
|
||||
// TODO: Implement only once AppUi can be told to constantly retrieve / reset icons...
|
||||
// var performanceProfile = Performance.getPerformancePreset();
|
||||
//
|
||||
// switch (performanceProfile) {
|
||||
// case 0:
|
||||
// return customIcon;
|
||||
// break;
|
||||
// case 1:
|
||||
// return lqIcon;
|
||||
// break;
|
||||
// case 2:
|
||||
// return mqIcon;
|
||||
// break;
|
||||
// case 3:
|
||||
// return hqIcon;
|
||||
// break;
|
||||
// default:
|
||||
// return customIcon;
|
||||
// }
|
||||
return customIcon;
|
||||
}
|
||||
|
||||
function startup() {
|
||||
ui = new AppUi({
|
||||
buttonName: BUTTON_NAME,
|
||||
sortOrder: 8,
|
||||
normalButton: getIcon(),
|
||||
activeButton: getIcon().replace('white', 'black'),
|
||||
home: GRAPHICS_QML_SOURCE
|
||||
});
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
}
|
||||
|
||||
//
|
||||
// Run the functions.
|
||||
//
|
||||
startup();
|
||||
Script.scriptEnding.connect(shutdown);
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
20
scripts/system/inventory/.gitignore
vendored
Normal file
20
scripts/system/inventory/.gitignore
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
24
scripts/system/inventory/README.md
Normal file
24
scripts/system/inventory/README.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
# inventory
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
5
scripts/system/inventory/babel.config.js
Normal file
5
scripts/system/inventory/babel.config.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
1
scripts/system/inventory/dist/css/app.a93e8b1f.css
vendored
Normal file
1
scripts/system/inventory/dist/css/app.a93e8b1f.css
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.draggable-card{background-color:#272727;margin:5px 0}.draggable-card .handle{width:40px!important}.top-level-folder{background-color:#272727}.top-level-folder .v-list-group__header__prepend-icon{background-color:rgba(0,0,0,.3);width:50px;height:50px;margin:5px 5px 7px 0!important;padding:5px 18px 5px 8px}.top-level-folder .handle{width:40px!important}.top-level-folder .folder-icon{margin-right:10px}.top-level-folder .folder-button{font-size:.795rem!important}.v-list-group .column-item{max-width:100%!important;margin-top:5px;margin-bottom:5px}.v-list-group .draggable-card{background-color:rgba(0,0,0,.3);padding-right:16px;padding-left:0!important}.v-list-group .draggable-card .handle{margin-right:16px}.app-version{text-align:center;color:hsla(0,0%,100%,.6);font-weight:lighter}.handle{background-color:rgba(0,0,0,.3)}.inventoryApp::-webkit-scrollbar{width:0!important}
|
5
scripts/system/inventory/dist/css/chunk-vendors.8540aa41.css
vendored
Normal file
5
scripts/system/inventory/dist/css/chunk-vendors.8540aa41.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
scripts/system/inventory/dist/favicon.ico
vendored
Normal file
BIN
scripts/system/inventory/dist/favicon.ico
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
1
scripts/system/inventory/dist/index.html
vendored
Normal file
1
scripts/system/inventory/dist/index.html
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=favicon.ico><title>Inventory</title><link rel=stylesheet href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css><link href=css/app.a93e8b1f.css rel=preload as=style><link href=css/chunk-vendors.8540aa41.css rel=preload as=style><link href=js/app.a3555a80.js rel=preload as=script><link href=js/chunk-vendors.a0f21a27.js rel=preload as=script><link href=css/chunk-vendors.8540aa41.css rel=stylesheet><link href=css/app.a93e8b1f.css rel=stylesheet></head><body><noscript><strong>We're sorry but Inventory doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=js/chunk-vendors.a0f21a27.js></script><script src=js/app.a3555a80.js></script></body></html>
|
1
scripts/system/inventory/dist/inventory-a-msg.svg
vendored
Normal file
1
scripts/system/inventory/dist/inventory-a-msg.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="#0c0" d="M17,14H19V17H22V19H19V22H17V19H14V17H17V14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.53C20.94,12.58 19.54,12 18,12A6,6 0 0,0 12,18C12,19.09 12.29,20.12 12.8,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>
|
After Width: | Height: | Size: 552 B |
1
scripts/system/inventory/dist/inventory-a.svg
vendored
Normal file
1
scripts/system/inventory/dist/inventory-a.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16.5,12C19,12 21,14 21,16.5C21,17.38 20.75,18.21 20.31,18.9L23.39,22L22,23.39L18.88,20.32C18.19,20.75 17.37,21 16.5,21C14,21 12,19 12,16.5C12,14 14,12 16.5,12M16.5,14A2.5,2.5 0 0,0 14,16.5A2.5,2.5 0 0,0 16.5,19A2.5,2.5 0 0,0 19,16.5A2.5,2.5 0 0,0 16.5,14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.03C20.85,11.21 18.82,10 16.5,10A6.5,6.5 0 0,0 10,16.5C10,18.25 10.69,19.83 11.81,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>
|
After Width: | Height: | Size: 761 B |
1
scripts/system/inventory/dist/inventory-i-msg.svg
vendored
Normal file
1
scripts/system/inventory/dist/inventory-i-msg.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="#0f0" d="M17,14H19V17H22V19H19V22H17V19H14V17H17V14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.53C20.94,12.58 19.54,12 18,12A6,6 0 0,0 12,18C12,19.09 12.29,20.12 12.8,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>
|
After Width: | Height: | Size: 552 B |
1
scripts/system/inventory/dist/inventory-i.svg
vendored
Normal file
1
scripts/system/inventory/dist/inventory-i.svg
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M16.5,12C19,12 21,14 21,16.5C21,17.38 20.75,18.21 20.31,18.9L23.39,22L22,23.39L18.88,20.32C18.19,20.75 17.37,21 16.5,21C14,21 12,19 12,16.5C12,14 14,12 16.5,12M16.5,14A2.5,2.5 0 0,0 14,16.5A2.5,2.5 0 0,0 16.5,19A2.5,2.5 0 0,0 19,16.5A2.5,2.5 0 0,0 16.5,14M10,2H14A2,2 0 0,1 16,4V6H20A2,2 0 0,1 22,8V13.03C20.85,11.21 18.82,10 16.5,10A6.5,6.5 0 0,0 10,16.5C10,18.25 10.69,19.83 11.81,21H4C2.89,21 2,20.1 2,19V8C2,6.89 2.89,6 4,6H8V4C8,2.89 8.89,2 10,2M14,6V4H10V6H14Z" /></svg>
|
After Width: | Height: | Size: 775 B |
299
scripts/system/inventory/dist/inventory.js
vendored
Normal file
299
scripts/system/inventory/dist/inventory.js
vendored
Normal file
|
@ -0,0 +1,299 @@
|
|||
//
|
||||
// inventory.js
|
||||
//
|
||||
// Created by kasenvr@gmail.com on 2 Apr 2020
|
||||
// Copyright 2020 Vircadia and contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
/* global AvatarList Clipboard console Controller Entities location Messages MyAvatar Script ScriptDiscoveryService Settings
|
||||
Tablet Vec3 Window */
|
||||
|
||||
(function () { // BEGIN LOCAL_SCOPE
|
||||
"use strict";
|
||||
var AppUi = Script.require('appUi');
|
||||
var ui;
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
|
||||
// VARIABLES
|
||||
var inventoryDataSettingString = "inventoryApp.data";
|
||||
var inventoryData;
|
||||
|
||||
var inventorySettingsString = "inventoryApp.settings";
|
||||
var inventorySettings;
|
||||
|
||||
var RECEIVING_ITEM_QUEUE_LIMIT = 5;
|
||||
var receivingItemQueue = [];
|
||||
|
||||
var NEARBY_USERS_SEARCH_RADIUS = 25;
|
||||
|
||||
|
||||
// APP EVENT AND MESSAGING ROUTING
|
||||
|
||||
function onWebAppEventReceived(event) {
|
||||
var eventJSON = JSON.parse(event);
|
||||
if (eventJSON.app === "inventory") { // This is our web app!
|
||||
// print("inventory.js received a web event: " + event);
|
||||
|
||||
if (eventJSON.command === "ready") {
|
||||
initializeInventoryApp();
|
||||
}
|
||||
|
||||
if (eventJSON.command === "web-to-script-inventory") {
|
||||
receiveInventory(eventJSON.data);
|
||||
}
|
||||
|
||||
if (eventJSON.command === "web-to-script-settings") {
|
||||
receiveSettings(eventJSON.data);
|
||||
}
|
||||
|
||||
if (eventJSON.command === "use-item") {
|
||||
useItem(eventJSON.data);
|
||||
}
|
||||
|
||||
if (eventJSON.command === "share-item") {
|
||||
shareItem(eventJSON.data);
|
||||
}
|
||||
|
||||
if (eventJSON.command === "web-to-script-request-nearby-users") {
|
||||
sendNearbyUsers();
|
||||
}
|
||||
|
||||
if (eventJSON.command === "web-to-script-request-receiving-item-queue") {
|
||||
sendReceivingItemQueue();
|
||||
}
|
||||
|
||||
if (eventJSON.command === "web-to-script-update-receiving-item-queue") {
|
||||
updateReceivingItemQueue(eventJSON.data);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tablet.webEventReceived.connect(onWebAppEventReceived);
|
||||
|
||||
function sendToWeb(command, data) {
|
||||
var dataToSend = {
|
||||
"app": "inventory",
|
||||
"command": command,
|
||||
"data": data
|
||||
};
|
||||
|
||||
tablet.emitScriptEvent(JSON.stringify(dataToSend));
|
||||
}
|
||||
|
||||
var inventoryMessagesChannel = "com.vircadia.inventory";
|
||||
|
||||
function onMessageReceived(channel, message, sender, localOnly) {
|
||||
if (channel === inventoryMessagesChannel) {
|
||||
var messageJSON = JSON.parse(message);
|
||||
// Window.alert("Passed 0 " + messageJSON.recipient + " vs " + MyAvatar.sessionUUID);
|
||||
if (messageJSON.command === "share-item"
|
||||
&& messageJSON.recipient === MyAvatar.sessionUUID) { // We are receiving an item.
|
||||
// Window.alert("Passed 1 " + messageJSON.recipient + " vs " + MyAvatar.sessionUUID);
|
||||
pushReceivedItemToQueue(sender, messageJSON.type, messageJSON.name, messageJSON.url);
|
||||
}
|
||||
}
|
||||
// print("Message received:");
|
||||
// print("- channel: " + channel);
|
||||
// print("- message: " + message);
|
||||
// print("- sender: " + sender);
|
||||
// print("- localOnly: " + localOnly);
|
||||
}
|
||||
|
||||
function sendMessage(dataToSend) {
|
||||
Messages.sendMessage(inventoryMessagesChannel, JSON.stringify(dataToSend));
|
||||
}
|
||||
|
||||
// END APP EVENT AND MESSAGING ROUTING
|
||||
|
||||
// SEND AND RECEIVE INVENTORY STATE
|
||||
|
||||
function receiveInventory(receivedInventoryData) {
|
||||
inventoryData = receivedInventoryData;
|
||||
saveInventory();
|
||||
}
|
||||
|
||||
function sendInventory() {
|
||||
sendToWeb("script-to-web-inventory", inventoryData);
|
||||
}
|
||||
|
||||
// END SEND AND RECEIVE INVENTORY STATE
|
||||
|
||||
// SEND AND RECEIVE SETTINGS STATE
|
||||
|
||||
function receiveSettings(receivedSettingsData) {
|
||||
inventorySettings = receivedSettingsData;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function sendSettings() {
|
||||
sendToWeb("script-to-web-settings", inventorySettings);
|
||||
}
|
||||
|
||||
// END SEND AND RECEIVE SETTINGS STATE
|
||||
|
||||
function saveInventory() {
|
||||
Settings.setValue(inventoryDataSettingString, inventoryData);
|
||||
}
|
||||
|
||||
function loadInventory() {
|
||||
inventoryData = Settings.getValue(inventoryDataSettingString);
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
Settings.setValue(inventorySettingsString, inventorySettings);
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
inventorySettings = Settings.getValue(inventorySettingsString);
|
||||
}
|
||||
|
||||
function pushReceivedItemToQueue(senderUUID, type, name, url) {
|
||||
console.info("Receiving an item:", name, "from:", senderUUID);
|
||||
var getAvatarData = AvatarList.getAvatar(senderUUID);
|
||||
var senderName = getAvatarData.sessionDisplayName;
|
||||
var senderDistance = Vec3.distance(MyAvatar.position, getAvatarData.position);
|
||||
|
||||
var packageRequest = {
|
||||
"senderUUID": senderUUID,
|
||||
"senderName": senderName,
|
||||
"senderDistance": senderDistance,
|
||||
"data": {
|
||||
"type": type,
|
||||
"name": name,
|
||||
"url": url
|
||||
}
|
||||
};
|
||||
|
||||
if (receivingItemQueue.length === RECEIVING_ITEM_QUEUE_LIMIT) {
|
||||
receivingItemQueue = receivingItemQueue.slice(1, RECEIVING_ITEM_QUEUE_LIMIT);
|
||||
}
|
||||
|
||||
receivingItemQueue.push(packageRequest);
|
||||
ui.messagesWaiting(receivingItemQueue.length > 0);
|
||||
}
|
||||
|
||||
function sendReceivingItemQueue() {
|
||||
sendToWeb("script-to-web-receiving-item-queue", receivingItemQueue);
|
||||
}
|
||||
|
||||
function updateReceivingItemQueue(data) {
|
||||
receivingItemQueue = data;
|
||||
ui.messagesWaiting(receivingItemQueue.length > 0);
|
||||
}
|
||||
|
||||
function sendNearbyUsers() {
|
||||
var nearbyUsers = AvatarList.getAvatarsInRange(MyAvatar.position, NEARBY_USERS_SEARCH_RADIUS);
|
||||
var nearbyUsersToSend = [];
|
||||
|
||||
nearbyUsers.forEach(function(user) {
|
||||
var objectToWrite;
|
||||
var aviDetails = AvatarList.getAvatar(user);
|
||||
var aviName = aviDetails.displayName;
|
||||
var aviDistance = Vec3.distance(MyAvatar.position, aviDetails.position);
|
||||
// Window.alert("aviName" + aviName + "user" + user + "MyAvatar.sessionUUID" + MyAvatar.sessionUUID);
|
||||
if (user !== MyAvatar.sessionUUID
|
||||
|| Controller.getValue(Controller.Hardware.Keyboard.Shift)) { // Don't add ourselves to the list!
|
||||
objectToWrite = { "name": aviName, "distance": aviDistance, "uuid": user };
|
||||
nearbyUsersToSend.push(objectToWrite);
|
||||
}
|
||||
});
|
||||
|
||||
sendToWeb("script-to-web-nearby-users", nearbyUsersToSend);
|
||||
}
|
||||
|
||||
function useItem(item) {
|
||||
|
||||
//TODO: Add animation support for avatars...?
|
||||
|
||||
// Convert the item.type before checking it...
|
||||
item.type = item.type.toUpperCase();
|
||||
|
||||
// Depending on the type, we decide how to load this item.
|
||||
if (item.type === "SCRIPT") {
|
||||
ScriptDiscoveryService.loadScript(item.url, true, false, false, true, false);
|
||||
}
|
||||
|
||||
if (item.type === "MODEL") {
|
||||
Entities.addEntity({
|
||||
type: "Model",
|
||||
position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1.5 })),
|
||||
rotation: MyAvatar.orientation,
|
||||
modelURL: item.url,
|
||||
collisionless: true
|
||||
});
|
||||
}
|
||||
|
||||
if (item.type === "AVATAR") {
|
||||
MyAvatar.useFullAvatarURL(item.url);
|
||||
}
|
||||
|
||||
if (item.type === "PLACE") {
|
||||
location.handleLookupString(item.url, true);
|
||||
}
|
||||
|
||||
if (item.type === "JSON") {
|
||||
var jsonToLoad = item.url;
|
||||
if (jsonToLoad) {
|
||||
if (Clipboard.importEntities(jsonToLoad)) {
|
||||
Clipboard.pasteEntities(
|
||||
Vec3.sum(
|
||||
MyAvatar.position,
|
||||
Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -1.5 })
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.type === "UNKNOWN") {
|
||||
// We don't know how to handle this yet.
|
||||
Window.alert("Unknown item type, unable to use.");
|
||||
}
|
||||
}
|
||||
|
||||
function shareItem(data) {
|
||||
data.command = "share-item";
|
||||
sendMessage(data);
|
||||
}
|
||||
|
||||
function initializeInventoryApp() {
|
||||
sendSettings();
|
||||
sendInventory();
|
||||
sendReceivingItemQueue();
|
||||
}
|
||||
|
||||
function onOpened() {
|
||||
}
|
||||
|
||||
function onClosed() {
|
||||
}
|
||||
|
||||
function startup() {
|
||||
|
||||
loadInventory();
|
||||
loadSettings();
|
||||
|
||||
Messages.messageReceived.connect(onMessageReceived);
|
||||
Messages.subscribe(inventoryMessagesChannel);
|
||||
|
||||
ui = new AppUi({
|
||||
buttonName: "INVENTORY",
|
||||
home: Script.resolvePath("index.html"),
|
||||
graphicsDirectory: Script.resolvePath("./"), // Where your button icons are located
|
||||
onOpened: onOpened,
|
||||
onClosed: onClosed
|
||||
});
|
||||
}
|
||||
|
||||
startup();
|
||||
|
||||
Script.scriptEnding.connect(function () {
|
||||
Messages.messageReceived.disconnect(onMessageReceived);
|
||||
Messages.unsubscribe(inventoryMessagesChannel);
|
||||
});
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
2
scripts/system/inventory/dist/js/app.a3555a80.js
vendored
Normal file
2
scripts/system/inventory/dist/js/app.a3555a80.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
scripts/system/inventory/dist/js/app.a3555a80.js.map
vendored
Normal file
1
scripts/system/inventory/dist/js/app.a3555a80.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
21
scripts/system/inventory/dist/js/chunk-vendors.a0f21a27.js
vendored
Normal file
21
scripts/system/inventory/dist/js/chunk-vendors.a0f21a27.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
scripts/system/inventory/dist/js/chunk-vendors.a0f21a27.js.map
vendored
Normal file
1
scripts/system/inventory/dist/js/chunk-vendors.a0f21a27.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
13049
scripts/system/inventory/package-lock.json
generated
Normal file
13049
scripts/system/inventory/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
52
scripts/system/inventory/package.json
Normal file
52
scripts/system/inventory/package.json
Normal file
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"name": "Inventory",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "^4.9.95",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^2.6.11",
|
||||
"vuedraggable": "^2.24.0",
|
||||
"vuetify": "^2.3.4",
|
||||
"vuex": "^3.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.3.1",
|
||||
"@vue/cli-plugin-eslint": "^4.3.1",
|
||||
"@vue/cli-service": "^4.3.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"sass": "^1.26.10",
|
||||
"sass-loader": "^8.0.0",
|
||||
"vue-cli-plugin-vuetify": "^2.0.7",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuetify-loader": "^1.6.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead",
|
||||
"ChromeAndroid > 55",
|
||||
"Chrome > 55"
|
||||
]
|
||||
}
|
BIN
scripts/system/inventory/public/favicon.ico
Normal file
BIN
scripts/system/inventory/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
30
scripts/system/inventory/public/index.html
Normal file
30
scripts/system/inventory/public/index.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
<!--
|
||||
//
|
||||
// index.html
|
||||
//
|
||||
// Created by kasenvr@gmail.com on 7 Apr 2020
|
||||
// Copyright 2020 Vircadia and contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue