Merge branch 'master' of https://github.com/highfidelity/hifi into poly_api
|
@ -46,8 +46,14 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
|
||||||
|
|
||||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
const QCommandLineOption helpOption = parser.addHelpOption();
|
||||||
|
|
||||||
const QCommandLineOption clientTypeOption(ASSIGNMENT_TYPE_OVERRIDE_OPTION,
|
QString typeDescription = "run single assignment client of given type\n# | Type\n============================";
|
||||||
"run single assignment client of given type", "type");
|
for (Assignment::Type type = Assignment::FirstType;
|
||||||
|
type != Assignment::AllTypes;
|
||||||
|
type = static_cast<Assignment::Type>(static_cast<int>(type) + 1)) {
|
||||||
|
typeDescription.append(QStringLiteral("\n%1 | %2").arg(QString::number(type), Assignment::typeToString(type)));
|
||||||
|
}
|
||||||
|
const QCommandLineOption clientTypeOption(ASSIGNMENT_TYPE_OVERRIDE_OPTION, typeDescription, "type");
|
||||||
|
|
||||||
parser.addOption(clientTypeOption);
|
parser.addOption(clientTypeOption);
|
||||||
|
|
||||||
const QCommandLineOption poolOption(ASSIGNMENT_POOL_OPTION, "set assignment pool", "pool-name");
|
const QCommandLineOption poolOption(ASSIGNMENT_POOL_OPTION, "set assignment pool", "pool-name");
|
||||||
|
|
|
@ -945,7 +945,7 @@ void DomainServer::createStaticAssignmentsForType(Assignment::Type type, const Q
|
||||||
|
|
||||||
void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes) {
|
void DomainServer::populateDefaultStaticAssignmentsExcludingTypes(const QSet<Assignment::Type>& excludedTypes) {
|
||||||
// enumerate over all assignment types and see if we've already excluded it
|
// enumerate over all assignment types and see if we've already excluded it
|
||||||
for (Assignment::Type defaultedType = Assignment::AudioMixerType;
|
for (Assignment::Type defaultedType = Assignment::FirstType;
|
||||||
defaultedType != Assignment::AllTypes;
|
defaultedType != Assignment::AllTypes;
|
||||||
defaultedType = static_cast<Assignment::Type>(static_cast<int>(defaultedType) + 1)) {
|
defaultedType = static_cast<Assignment::Type>(static_cast<int>(defaultedType) + 1)) {
|
||||||
if (!excludedTypes.contains(defaultedType) && defaultedType != Assignment::AgentType) {
|
if (!excludedTypes.contains(defaultedType) && defaultedType != Assignment::AgentType) {
|
||||||
|
|
|
@ -59,6 +59,7 @@ Rectangle {
|
||||||
} else if (walletStatus === 2) {
|
} else if (walletStatus === 2) {
|
||||||
if (root.activeView !== "passphraseModal") {
|
if (root.activeView !== "passphraseModal") {
|
||||||
root.activeView = "passphraseModal";
|
root.activeView = "passphraseModal";
|
||||||
|
UserActivityLogger.commercePassphraseEntry("marketplace checkout");
|
||||||
}
|
}
|
||||||
} else if (walletStatus === 3) {
|
} else if (walletStatus === 3) {
|
||||||
authSuccessStep();
|
authSuccessStep();
|
||||||
|
|
|
@ -54,6 +54,7 @@ Rectangle {
|
||||||
} else if (walletStatus === 2) {
|
} else if (walletStatus === 2) {
|
||||||
if (root.activeView !== "passphraseModal") {
|
if (root.activeView !== "passphraseModal") {
|
||||||
root.activeView = "passphraseModal";
|
root.activeView = "passphraseModal";
|
||||||
|
UserActivityLogger.commercePassphraseEntry("marketplace purchases");
|
||||||
}
|
}
|
||||||
} else if (walletStatus === 3) {
|
} else if (walletStatus === 3) {
|
||||||
if ((Settings.getValue("isFirstUseOfPurchases", true) || root.isDebuggingFirstUseTutorial) && root.activeView !== "firstUseTutorial") {
|
if ((Settings.getValue("isFirstUseOfPurchases", true) || root.isDebuggingFirstUseTutorial) && root.activeView !== "firstUseTutorial") {
|
||||||
|
|
|
@ -25,10 +25,6 @@ Item {
|
||||||
|
|
||||||
id: root;
|
id: root;
|
||||||
|
|
||||||
SecurityImageModel {
|
|
||||||
id: securityImageModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Username Text
|
// Username Text
|
||||||
RalewayRegular {
|
RalewayRegular {
|
||||||
id: usernameText;
|
id: usernameText;
|
||||||
|
|
|
@ -50,8 +50,10 @@ Item {
|
||||||
submitPassphraseInputButton.enabled = true;
|
submitPassphraseInputButton.enabled = true;
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
errorText.text = "Authentication failed - please try again.";
|
errorText.text = "Authentication failed - please try again.";
|
||||||
|
UserActivityLogger.commercePassphraseAuthenticationStatus("auth failure");
|
||||||
} else {
|
} else {
|
||||||
sendSignalToParent({method: 'authSuccess'});;
|
sendSignalToParent({method: 'authSuccess'});
|
||||||
|
UserActivityLogger.commercePassphraseAuthenticationStatus("auth success");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -336,6 +338,7 @@ Item {
|
||||||
text: "Cancel"
|
text: "Cancel"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
|
sendSignalToParent({method: 'passphrasePopup_cancelClicked'});
|
||||||
|
UserActivityLogger.commercePassphraseAuthenticationStatus("passphrase modal cancelled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,6 @@ Item {
|
||||||
id: root;
|
id: root;
|
||||||
property bool justSubmitted: false;
|
property bool justSubmitted: false;
|
||||||
|
|
||||||
SecurityImageModel {
|
|
||||||
id: securityImageModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
Hifi.QmlCommerce {
|
Hifi.QmlCommerce {
|
||||||
id: commerce;
|
id: commerce;
|
||||||
|
|
||||||
|
@ -213,4 +209,8 @@ Item {
|
||||||
securityImageSubmitButton.enabled = true;
|
securityImageSubmitButton.enabled = true;
|
||||||
securityImageSubmitButton.text = "Submit";
|
securityImageSubmitButton.text = "Submit";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initModel() {
|
||||||
|
securityImageSelection.initModel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,29 +15,28 @@ import QtQuick 2.5
|
||||||
|
|
||||||
ListModel {
|
ListModel {
|
||||||
id: root;
|
id: root;
|
||||||
ListElement{
|
|
||||||
sourcePath: "images/01.jpg"
|
function initModel() {
|
||||||
securityImageEnumValue: 1;
|
var array = [];
|
||||||
|
while (array.length < 6) {
|
||||||
|
// We currently have 34 security images to choose from
|
||||||
|
var randomNumber = Math.floor(Math.random() * 34) + 1;
|
||||||
|
if (array.indexOf(randomNumber) > -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
array[array.length] = randomNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
var modelElement;
|
||||||
|
|
||||||
|
for (var i = 0; i < 6; i++) {
|
||||||
|
modelElement = { "sourcePath":"images/" + addLeadingZero(array[i]) + ".jpg", "securityImageEnumValue": (i + 1) }
|
||||||
|
root.insert(i, modelElement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ListElement{
|
|
||||||
sourcePath: "images/02.jpg"
|
function addLeadingZero(n) {
|
||||||
securityImageEnumValue: 2;
|
return n < 10 ? '0' + n : '' + n;
|
||||||
}
|
|
||||||
ListElement{
|
|
||||||
sourcePath: "images/03.jpg"
|
|
||||||
securityImageEnumValue: 3;
|
|
||||||
}
|
|
||||||
ListElement{
|
|
||||||
sourcePath: "images/04.jpg"
|
|
||||||
securityImageEnumValue: 4;
|
|
||||||
}
|
|
||||||
ListElement{
|
|
||||||
sourcePath: "images/05.jpg"
|
|
||||||
securityImageEnumValue: 5;
|
|
||||||
}
|
|
||||||
ListElement{
|
|
||||||
sourcePath: "images/06.jpg"
|
|
||||||
securityImageEnumValue: 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getImagePathFromImageID(imageID) {
|
function getImagePathFromImageID(imageID) {
|
||||||
|
|
|
@ -95,6 +95,10 @@ Item {
|
||||||
function getSelectedImageIndex() {
|
function getSelectedImageIndex() {
|
||||||
return gridModel.get(securityImageGrid.currentIndex).securityImageEnumValue;
|
return gridModel.get(securityImageGrid.currentIndex).securityImageEnumValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initModel() {
|
||||||
|
gridModel.initModel();
|
||||||
|
}
|
||||||
//
|
//
|
||||||
// FUNCTION DEFINITIONS END
|
// FUNCTION DEFINITIONS END
|
||||||
//
|
//
|
||||||
|
|
|
@ -57,6 +57,7 @@ Rectangle {
|
||||||
} else if (walletStatus === 2) {
|
} else if (walletStatus === 2) {
|
||||||
if (root.activeView !== "passphraseModal") {
|
if (root.activeView !== "passphraseModal") {
|
||||||
root.activeView = "passphraseModal";
|
root.activeView = "passphraseModal";
|
||||||
|
UserActivityLogger.commercePassphraseEntry("wallet app");
|
||||||
}
|
}
|
||||||
} else if (walletStatus === 3) {
|
} else if (walletStatus === 3) {
|
||||||
root.activeView = "walletHome";
|
root.activeView = "walletHome";
|
||||||
|
@ -82,10 +83,6 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SecurityImageModel {
|
|
||||||
id: securityImageModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
HifiCommerceCommon.CommerceLightbox {
|
HifiCommerceCommon.CommerceLightbox {
|
||||||
id: lightboxPopup;
|
id: lightboxPopup;
|
||||||
visible: false;
|
visible: false;
|
||||||
|
@ -342,6 +339,7 @@ Rectangle {
|
||||||
passphraseChange.clearPassphraseFields();
|
passphraseChange.clearPassphraseFields();
|
||||||
passphraseChange.resetSubmitButton();
|
passphraseChange.resetSubmitButton();
|
||||||
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
|
} else if (msg.method === 'walletSecurity_changeSecurityImage') {
|
||||||
|
securityImageChange.initModel();
|
||||||
root.activeView = "securityImageChange";
|
root.activeView = "securityImageChange";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,6 +236,7 @@ Item {
|
||||||
height: 50;
|
height: 50;
|
||||||
text: "Set Up Wallet";
|
text: "Set Up Wallet";
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
securityImageSelection.initModel();
|
||||||
root.activeView = "step_2";
|
root.activeView = "step_2";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 35 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/07.jpg
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/08.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/09.jpg
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/10.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/11.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/12.jpg
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/13.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/14.jpg
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/15.jpg
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/16.jpg
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/17.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/18.jpg
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/19.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/20.jpg
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/21.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/22.jpg
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/23.jpg
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/24.jpg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/25.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/26.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/27.jpg
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/28.jpg
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/29.jpg
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/30.jpg
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/31.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/32.jpg
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/33.jpg
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
interface/resources/qml/hifi/commerce/wallet/images/34.jpg
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
interface/resources/qml/js/Utils.jsc
Normal file
|
@ -1498,6 +1498,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(getEntities()->getTree().get(), &EntityTree::deletingEntity, [=](const EntityItemID& entityItemID) {
|
||||||
|
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||||
|
auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr;
|
||||||
|
if (myAvatar) {
|
||||||
|
myAvatar->clearAvatarEntity(entityItemID);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Keyboard focus handling for Web overlays.
|
// Keyboard focus handling for Web overlays.
|
||||||
auto overlays = &(qApp->getOverlays());
|
auto overlays = &(qApp->getOverlays());
|
||||||
connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) {
|
connect(overlays, &Overlays::overlayDeleted, [=](const OverlayID& overlayID) {
|
||||||
|
@ -3224,8 +3232,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Application::keyReleaseEvent(QKeyEvent* event) {
|
void Application::keyReleaseEvent(QKeyEvent* event) {
|
||||||
_keysPressed.remove(event->key());
|
_keysPressed.remove(event->key());
|
||||||
|
|
||||||
|
@ -4850,8 +4856,7 @@ void Application::update(float deltaTime) {
|
||||||
if (_physicsEnabled) {
|
if (_physicsEnabled) {
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "PreStep");
|
PROFILE_RANGE(simulation_physics, "PreStep");
|
||||||
|
PerformanceTimer perfTimer("preStep)");
|
||||||
PerformanceTimer perfTimer("updateStates)");
|
|
||||||
static VectorOfMotionStates motionStates;
|
static VectorOfMotionStates motionStates;
|
||||||
_entitySimulation->getObjectsToRemoveFromPhysics(motionStates);
|
_entitySimulation->getObjectsToRemoveFromPhysics(motionStates);
|
||||||
_physicsEngine->removeObjects(motionStates);
|
_physicsEngine->removeObjects(motionStates);
|
||||||
|
@ -4884,22 +4889,22 @@ void Application::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "Step");
|
PROFILE_RANGE(simulation_physics, "Step");
|
||||||
PerformanceTimer perfTimer("stepSimulation");
|
PerformanceTimer perfTimer("step");
|
||||||
getEntities()->getTree()->withWriteLock([&] {
|
getEntities()->getTree()->withWriteLock([&] {
|
||||||
_physicsEngine->stepSimulation();
|
_physicsEngine->stepSimulation();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "PostStep");
|
PROFILE_RANGE(simulation_physics, "PostStep");
|
||||||
PerformanceTimer perfTimer("harvestChanges");
|
PerformanceTimer perfTimer("postStep");
|
||||||
if (_physicsEngine->hasOutgoingChanges()) {
|
if (_physicsEngine->hasOutgoingChanges()) {
|
||||||
// grab the collision events BEFORE handleOutgoingChanges() because at this point
|
// grab the collision events BEFORE handleOutgoingChanges() because at this point
|
||||||
// we have a better idea of which objects we own or should own.
|
// we have a better idea of which objects we own or should own.
|
||||||
auto& collisionEvents = _physicsEngine->getCollisionEvents();
|
auto& collisionEvents = _physicsEngine->getCollisionEvents();
|
||||||
|
|
||||||
getEntities()->getTree()->withWriteLock([&] {
|
getEntities()->getTree()->withWriteLock([&] {
|
||||||
PROFILE_RANGE(simulation_physics, "Harvest");
|
PROFILE_RANGE(simulation_physics, "HandleChanges");
|
||||||
PerformanceTimer perfTimer("handleOutgoingChanges");
|
PerformanceTimer perfTimer("handleChanges");
|
||||||
|
|
||||||
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
|
const VectorOfMotionStates& outgoingChanges = _physicsEngine->getChangedMotionStates();
|
||||||
_entitySimulation->handleChangedMotionStates(outgoingChanges);
|
_entitySimulation->handleChangedMotionStates(outgoingChanges);
|
||||||
|
@ -4910,17 +4915,15 @@ void Application::update(float deltaTime) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!_aboutToQuit) {
|
if (!_aboutToQuit) {
|
||||||
// handleCollisionEvents() AFTER handleOutgoinChanges()
|
// handleCollisionEvents() AFTER handleOutgoingChanges()
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "CollisionEvents");
|
PROFILE_RANGE(simulation_physics, "CollisionEvents");
|
||||||
PerformanceTimer perfTimer("entities");
|
|
||||||
avatarManager->handleCollisionEvents(collisionEvents);
|
avatarManager->handleCollisionEvents(collisionEvents);
|
||||||
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
|
// Collision events (and their scripts) must not be handled when we're locked, above. (That would risk
|
||||||
// deadlock.)
|
// deadlock.)
|
||||||
_entitySimulation->handleCollisionEvents(collisionEvents);
|
_entitySimulation->handleCollisionEvents(collisionEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
PROFILE_RANGE(simulation_physics, "UpdateEntities");
|
|
||||||
// NOTE: the getEntities()->update() call below will wait for lock
|
// NOTE: the getEntities()->update() call below will wait for lock
|
||||||
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
// and will simulate entity motion (the EntityTree has been given an EntitySimulation).
|
||||||
getEntities()->update(true); // update the models...
|
getEntities()->update(true); // update the models...
|
||||||
|
@ -4931,7 +4934,8 @@ void Application::update(float deltaTime) {
|
||||||
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
if (PerformanceTimer::isActive() &&
|
||||||
|
Menu::getInstance()->isOptionChecked(MenuOption::DisplayDebugTimingDetails) &&
|
||||||
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) {
|
Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming)) {
|
||||||
_physicsEngine->harvestPerformanceStats();
|
_physicsEngine->harvestPerformanceStats();
|
||||||
}
|
}
|
||||||
|
@ -7509,4 +7513,9 @@ void Application::setAvatarOverrideUrl(const QUrl& url, bool save) {
|
||||||
_avatarOverrideUrl = url;
|
_avatarOverrideUrl = url;
|
||||||
_saveAvatarOverrideUrl = save;
|
_saveAvatarOverrideUrl = save;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::saveNextPhysicsStats(QString filename) {
|
||||||
|
_physicsEngine->saveNextPhysicsStats(filename);
|
||||||
|
}
|
||||||
|
|
||||||
#include "Application.moc"
|
#include "Application.moc"
|
||||||
|
|
|
@ -280,6 +280,7 @@ public:
|
||||||
void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; }
|
void clearAvatarOverrideUrl() { _avatarOverrideUrl = QUrl(); _saveAvatarOverrideUrl = false; }
|
||||||
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
|
QUrl getAvatarOverrideUrl() { return _avatarOverrideUrl; }
|
||||||
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
|
bool getSaveAvatarOverrideUrl() { return _saveAvatarOverrideUrl; }
|
||||||
|
void saveNextPhysicsStats(QString filename);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void svoImportRequested(const QString& url);
|
void svoImportRequested(const QString& url);
|
||||||
|
@ -432,6 +433,7 @@ private slots:
|
||||||
|
|
||||||
void handleSandboxStatus(QNetworkReply* reply);
|
void handleSandboxStatus(QNetworkReply* reply);
|
||||||
void switchDisplayMode();
|
void switchDisplayMode();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void initDisplay();
|
static void initDisplay();
|
||||||
void init();
|
void init();
|
||||||
|
|
|
@ -645,7 +645,8 @@ Menu::Menu() {
|
||||||
// Developer > Timing >>>
|
// Developer > Timing >>>
|
||||||
MenuWrapper* timingMenu = developerMenu->addMenu("Timing");
|
MenuWrapper* timingMenu = developerMenu->addMenu("Timing");
|
||||||
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer");
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false,
|
||||||
|
qApp, SLOT(enablePerfStats(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true);
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false);
|
||||||
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false);
|
addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false);
|
||||||
|
|
|
@ -11,11 +11,12 @@
|
||||||
#include <QtCore/QLoggingCategory>
|
#include <QtCore/QLoggingCategory>
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
|
#include <shared/FileUtils.h>
|
||||||
#include <shared/QtHelpers.h>
|
#include <shared/QtHelpers.h>
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <Trace.h>
|
|
||||||
#include <StatTracker.h>
|
|
||||||
#include <OffscreenUi.h>
|
#include <OffscreenUi.h>
|
||||||
|
#include <StatTracker.h>
|
||||||
|
#include <Trace.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
|
||||||
|
@ -141,6 +142,15 @@ void TestScriptingInterface::endTraceEvent(QString name) {
|
||||||
tracing::traceEvent(trace_test(), name, tracing::DurationEnd);
|
tracing::traceEvent(trace_test(), name, tracing::DurationEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestScriptingInterface::savePhysicsSimulationStats(QString originalPath) {
|
||||||
|
QString path = FileUtils::replaceDateTimeTokens(originalPath);
|
||||||
|
path = FileUtils::computeDocumentPath(path);
|
||||||
|
if (!FileUtils::canCreateFile(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qApp->saveNextPhysicsStats(path);
|
||||||
|
}
|
||||||
|
|
||||||
void TestScriptingInterface::profileRange(const QString& name, QScriptValue fn) {
|
void TestScriptingInterface::profileRange(const QString& name, QScriptValue fn) {
|
||||||
PROFILE_RANGE(script, name);
|
PROFILE_RANGE(script, name);
|
||||||
fn.call();
|
fn.call();
|
||||||
|
|
|
@ -71,6 +71,11 @@ public slots:
|
||||||
|
|
||||||
void endTraceEvent(QString name);
|
void endTraceEvent(QString name);
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Write detailed timing stats of next physics stepSimulation() to filename
|
||||||
|
*/
|
||||||
|
void savePhysicsSimulationStats(QString filename);
|
||||||
|
|
||||||
Q_INVOKABLE void profileRange(const QString& name, QScriptValue function);
|
Q_INVOKABLE void profileRange(const QString& name, QScriptValue function);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -78,6 +78,8 @@ bool Stats::includeTimingRecord(const QString& name) {
|
||||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||||
} else if (name.startsWith("/paintGL/")) {
|
} else if (name.startsWith("/paintGL/")) {
|
||||||
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPaintGLTiming);
|
||||||
|
} else if (name.startsWith("step/")) {
|
||||||
|
return Menu::getInstance()->isOptionChecked(MenuOption::ExpandPhysicsSimulationTiming);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,10 +251,10 @@ void EntityTreeRenderer::shutdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction) {
|
void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||||
PROFILE_RANGE_EX(simulation_physics, "Add", 0xffff00ff, (uint64_t)_entitiesToAdd.size());
|
PROFILE_RANGE_EX(simulation_physics, "AddToScene", 0xffff00ff, (uint64_t)_entitiesToAdd.size());
|
||||||
PerformanceTimer pt("add");
|
PerformanceTimer pt("add");
|
||||||
// Clear any expired entities
|
// Clear any expired entities
|
||||||
// FIXME should be able to use std::remove_if, but it fails due to some
|
// FIXME should be able to use std::remove_if, but it fails due to some
|
||||||
// weird compilation error related to EntityItemID assignment operators
|
// weird compilation error related to EntityItemID assignment operators
|
||||||
for (auto itr = _entitiesToAdd.begin(); _entitiesToAdd.end() != itr; ) {
|
for (auto itr = _entitiesToAdd.begin(); _entitiesToAdd.end() != itr; ) {
|
||||||
if (itr->second.expired()) {
|
if (itr->second.expired()) {
|
||||||
|
@ -272,7 +272,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path to the parent transforms is not valid,
|
// Path to the parent transforms is not valid,
|
||||||
// don't add to the scene graph yet
|
// don't add to the scene graph yet
|
||||||
if (!entity->isParentPathComplete()) {
|
if (!entity->isParentPathComplete()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -296,7 +296,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction) {
|
void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction) {
|
||||||
PROFILE_RANGE_EX(simulation_physics, "Change", 0xffff00ff, (uint64_t)_changedEntities.size());
|
PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size());
|
||||||
PerformanceTimer pt("change");
|
PerformanceTimer pt("change");
|
||||||
std::unordered_set<EntityItemID> changedEntities;
|
std::unordered_set<EntityItemID> changedEntities;
|
||||||
_changedEntitiesGuard.withWriteLock([&] {
|
_changedEntitiesGuard.withWriteLock([&] {
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <Octree.h>
|
#include <Octree.h>
|
||||||
#include <PhysicsHelpers.h>
|
#include <PhysicsHelpers.h>
|
||||||
|
#include <Profile.h>
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
#include <SharedUtil.h> // usecTimestampNow()
|
#include <SharedUtil.h> // usecTimestampNow()
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
|
@ -114,6 +115,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
||||||
requestedProperties += PROP_EDITION_NUMBER;
|
requestedProperties += PROP_EDITION_NUMBER;
|
||||||
requestedProperties += PROP_ENTITY_INSTANCE_NUMBER;
|
requestedProperties += PROP_ENTITY_INSTANCE_NUMBER;
|
||||||
requestedProperties += PROP_CERTIFICATE_ID;
|
requestedProperties += PROP_CERTIFICATE_ID;
|
||||||
|
requestedProperties += PROP_STATIC_CERTIFICATE_VERSION;
|
||||||
|
|
||||||
requestedProperties += PROP_NAME;
|
requestedProperties += PROP_NAME;
|
||||||
requestedProperties += PROP_HREF;
|
requestedProperties += PROP_HREF;
|
||||||
|
@ -271,6 +273,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
||||||
APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, getEditionNumber());
|
APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, getEditionNumber());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, getEntityInstanceNumber());
|
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, getEntityInstanceNumber());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, getCertificateID());
|
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, getCertificateID());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, getStaticCertificateVersion());
|
||||||
|
|
||||||
APPEND_ENTITY_PROPERTY(PROP_NAME, getName());
|
APPEND_ENTITY_PROPERTY(PROP_NAME, getName());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
|
APPEND_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, getCollisionSoundURL());
|
||||||
|
@ -819,6 +822,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
READ_ENTITY_PROPERTY(PROP_EDITION_NUMBER, quint32, setEditionNumber);
|
READ_ENTITY_PROPERTY(PROP_EDITION_NUMBER, quint32, setEditionNumber);
|
||||||
READ_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, quint32, setEntityInstanceNumber);
|
READ_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, quint32, setEntityInstanceNumber);
|
||||||
READ_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, QString, setCertificateID);
|
READ_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, QString, setCertificateID);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion);
|
||||||
|
|
||||||
READ_ENTITY_PROPERTY(PROP_NAME, QString, setName);
|
READ_ENTITY_PROPERTY(PROP_NAME, QString, setName);
|
||||||
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
READ_ENTITY_PROPERTY(PROP_COLLISION_SOUND_URL, QString, setCollisionSoundURL);
|
||||||
|
@ -984,6 +988,7 @@ void EntityItem::setCollisionSoundURL(const QString& value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::simulate(const quint64& now) {
|
void EntityItem::simulate(const quint64& now) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "Simulate");
|
||||||
if (getLastSimulated() == 0) {
|
if (getLastSimulated() == 0) {
|
||||||
setLastSimulated(now);
|
setLastSimulated(now);
|
||||||
}
|
}
|
||||||
|
@ -1039,6 +1044,7 @@ void EntityItem::simulate(const quint64& now) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityItem::stepKinematicMotion(float timeElapsed) {
|
bool EntityItem::stepKinematicMotion(float timeElapsed) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "StepKinematicMotion");
|
||||||
// get all the data
|
// get all the data
|
||||||
Transform transform;
|
Transform transform;
|
||||||
glm::vec3 linearVelocity;
|
glm::vec3 linearVelocity;
|
||||||
|
@ -1249,6 +1255,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(editionNumber, getEditionNumber);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(editionNumber, getEditionNumber);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityInstanceNumber, getEntityInstanceNumber);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(entityInstanceNumber, getEntityInstanceNumber);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(certificateID, getCertificateID);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(certificateID, getCertificateID);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(staticCertificateVersion, getStaticCertificateVersion);
|
||||||
|
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(name, getName);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(href, getHref);
|
||||||
|
@ -1356,6 +1363,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(editionNumber, setEditionNumber);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(editionNumber, setEditionNumber);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(entityInstanceNumber, setEntityInstanceNumber);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(entityInstanceNumber, setEntityInstanceNumber);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(certificateID, setCertificateID);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(certificateID, setCertificateID);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(staticCertificateVersion, setStaticCertificateVersion);
|
||||||
|
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(name, setName);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(href, setHref);
|
||||||
|
@ -1626,6 +1634,10 @@ void EntityItem::setParentID(const QUuid& value) {
|
||||||
newParentNoBootstrapping |= Simulation::NO_BOOTSTRAPPING;
|
newParentNoBootstrapping |= Simulation::NO_BOOTSTRAPPING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!oldParentID.isNull() && (oldParentID == Physics::getSessionUUID() || oldParentID == AVATAR_SELF_ID)) {
|
||||||
|
oldParentNoBootstrapping |= Simulation::NO_BOOTSTRAPPING;
|
||||||
|
}
|
||||||
|
|
||||||
if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) {
|
if ((bool)(oldParentNoBootstrapping ^ newParentNoBootstrapping)) {
|
||||||
if ((bool)(newParentNoBootstrapping & Simulation::NO_BOOTSTRAPPING)) {
|
if ((bool)(newParentNoBootstrapping & Simulation::NO_BOOTSTRAPPING)) {
|
||||||
markDirtyFlags(Simulation::NO_BOOTSTRAPPING);
|
markDirtyFlags(Simulation::NO_BOOTSTRAPPING);
|
||||||
|
@ -2783,6 +2795,7 @@ DEFINE_PROPERTY_ACCESSOR(QString, MarketplaceID, marketplaceID)
|
||||||
DEFINE_PROPERTY_ACCESSOR(quint32, EditionNumber, editionNumber)
|
DEFINE_PROPERTY_ACCESSOR(quint32, EditionNumber, editionNumber)
|
||||||
DEFINE_PROPERTY_ACCESSOR(quint32, EntityInstanceNumber, entityInstanceNumber)
|
DEFINE_PROPERTY_ACCESSOR(quint32, EntityInstanceNumber, entityInstanceNumber)
|
||||||
DEFINE_PROPERTY_ACCESSOR(QString, CertificateID, certificateID)
|
DEFINE_PROPERTY_ACCESSOR(QString, CertificateID, certificateID)
|
||||||
|
DEFINE_PROPERTY_ACCESSOR(quint32, StaticCertificateVersion, staticCertificateVersion)
|
||||||
|
|
||||||
uint32_t EntityItem::getDirtyFlags() const {
|
uint32_t EntityItem::getDirtyFlags() const {
|
||||||
uint32_t result;
|
uint32_t result;
|
||||||
|
|
|
@ -328,6 +328,8 @@ public:
|
||||||
void setEntityInstanceNumber(const quint32&);
|
void setEntityInstanceNumber(const quint32&);
|
||||||
QString getCertificateID() const;
|
QString getCertificateID() const;
|
||||||
void setCertificateID(const QString& value);
|
void setCertificateID(const QString& value);
|
||||||
|
quint32 getStaticCertificateVersion() const;
|
||||||
|
void setStaticCertificateVersion(const quint32&);
|
||||||
|
|
||||||
// TODO: get rid of users of getRadius()...
|
// TODO: get rid of users of getRadius()...
|
||||||
float getRadius() const;
|
float getRadius() const;
|
||||||
|
@ -547,6 +549,7 @@ protected:
|
||||||
quint32 _editionNumber { ENTITY_ITEM_DEFAULT_EDITION_NUMBER };
|
quint32 _editionNumber { ENTITY_ITEM_DEFAULT_EDITION_NUMBER };
|
||||||
quint32 _entityInstanceNumber { ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER };
|
quint32 _entityInstanceNumber { ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER };
|
||||||
QString _marketplaceID { ENTITY_ITEM_DEFAULT_MARKETPLACE_ID };
|
QString _marketplaceID { ENTITY_ITEM_DEFAULT_MARKETPLACE_ID };
|
||||||
|
quint32 _staticCertificateVersion { ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION };
|
||||||
|
|
||||||
|
|
||||||
// NOTE: Damping is applied like this: v *= pow(1 - damping, dt)
|
// NOTE: Damping is applied like this: v *= pow(1 - damping, dt)
|
||||||
|
|
|
@ -324,6 +324,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
CHECK_PROPERTY_CHANGE(PROP_EDITION_NUMBER, editionNumber);
|
CHECK_PROPERTY_CHANGE(PROP_EDITION_NUMBER, editionNumber);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_ENTITY_INSTANCE_NUMBER, entityInstanceNumber);
|
CHECK_PROPERTY_CHANGE(PROP_ENTITY_INSTANCE_NUMBER, entityInstanceNumber);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_CERTIFICATE_ID, certificateID);
|
CHECK_PROPERTY_CHANGE(PROP_CERTIFICATE_ID, certificateID);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_STATIC_CERTIFICATE_VERSION, staticCertificateVersion);
|
||||||
|
|
||||||
CHECK_PROPERTY_CHANGE(PROP_NAME, name);
|
CHECK_PROPERTY_CHANGE(PROP_NAME, name);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode);
|
CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode);
|
||||||
|
@ -459,6 +460,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EDITION_NUMBER, editionNumber);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EDITION_NUMBER, editionNumber);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ENTITY_INSTANCE_NUMBER, entityInstanceNumber);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ENTITY_INSTANCE_NUMBER, entityInstanceNumber);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CERTIFICATE_ID, certificateID);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CERTIFICATE_ID, certificateID);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STATIC_CERTIFICATE_VERSION, staticCertificateVersion);
|
||||||
|
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL);
|
||||||
|
@ -742,6 +744,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(editionNumber, quint32, setEditionNumber);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(editionNumber, quint32, setEditionNumber);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(entityInstanceNumber, quint32, setEntityInstanceNumber);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(entityInstanceNumber, quint32, setEntityInstanceNumber);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(certificateID, QString, setCertificateID);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(certificateID, QString, setCertificateID);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(staticCertificateVersion, quint32, setStaticCertificateVersion);
|
||||||
|
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL);
|
||||||
|
@ -899,6 +902,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
||||||
COPY_PROPERTY_IF_CHANGED(editionNumber);
|
COPY_PROPERTY_IF_CHANGED(editionNumber);
|
||||||
COPY_PROPERTY_IF_CHANGED(entityInstanceNumber);
|
COPY_PROPERTY_IF_CHANGED(entityInstanceNumber);
|
||||||
COPY_PROPERTY_IF_CHANGED(certificateID);
|
COPY_PROPERTY_IF_CHANGED(certificateID);
|
||||||
|
COPY_PROPERTY_IF_CHANGED(staticCertificateVersion);
|
||||||
|
|
||||||
COPY_PROPERTY_IF_CHANGED(name);
|
COPY_PROPERTY_IF_CHANGED(name);
|
||||||
COPY_PROPERTY_IF_CHANGED(collisionSoundURL);
|
COPY_PROPERTY_IF_CHANGED(collisionSoundURL);
|
||||||
|
@ -1089,6 +1093,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
||||||
ADD_PROPERTY_TO_MAP(PROP_EDITION_NUMBER, EditionNumber, editionNumber, quint32);
|
ADD_PROPERTY_TO_MAP(PROP_EDITION_NUMBER, EditionNumber, editionNumber, quint32);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_ENTITY_INSTANCE_NUMBER, EntityInstanceNumber, entityInstanceNumber, quint32);
|
ADD_PROPERTY_TO_MAP(PROP_ENTITY_INSTANCE_NUMBER, EntityInstanceNumber, entityInstanceNumber, quint32);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString);
|
ADD_PROPERTY_TO_MAP(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_STATIC_CERTIFICATE_VERSION, StaticCertificateVersion, staticCertificateVersion, quint32);
|
||||||
|
|
||||||
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor);
|
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float);
|
ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float);
|
||||||
|
@ -1477,6 +1482,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
||||||
APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, properties.getEditionNumber());
|
APPEND_ENTITY_PROPERTY(PROP_EDITION_NUMBER, properties.getEditionNumber());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber());
|
APPEND_ENTITY_PROPERTY(PROP_ENTITY_INSTANCE_NUMBER, properties.getEntityInstanceNumber());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID());
|
APPEND_ENTITY_PROPERTY(PROP_CERTIFICATE_ID, properties.getCertificateID());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (propertyCount > 0) {
|
if (propertyCount > 0) {
|
||||||
|
@ -1828,6 +1834,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EDITION_NUMBER, quint32, setEditionNumber);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EDITION_NUMBER, quint32, setEditionNumber);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ENTITY_INSTANCE_NUMBER, quint32, setEntityInstanceNumber);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ENTITY_INSTANCE_NUMBER, quint32, setEntityInstanceNumber);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_ID, QString, setCertificateID);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CERTIFICATE_ID, QString, setCertificateID);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion);
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
@ -1995,6 +2002,7 @@ void EntityItemProperties::markAllChanged() {
|
||||||
_editionNumberChanged = true;
|
_editionNumberChanged = true;
|
||||||
_entityInstanceNumberChanged = true;
|
_entityInstanceNumberChanged = true;
|
||||||
_certificateIDChanged = true;
|
_certificateIDChanged = true;
|
||||||
|
_staticCertificateVersionChanged = true;
|
||||||
|
|
||||||
_keyLight.markAllChanged();
|
_keyLight.markAllChanged();
|
||||||
|
|
||||||
|
@ -2337,6 +2345,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
||||||
if (certificateIDChanged()) {
|
if (certificateIDChanged()) {
|
||||||
out += "certificateID";
|
out += "certificateID";
|
||||||
}
|
}
|
||||||
|
if (staticCertificateVersionChanged()) {
|
||||||
|
out += "staticCertificateVersion";
|
||||||
|
}
|
||||||
|
|
||||||
if (backgroundModeChanged()) {
|
if (backgroundModeChanged()) {
|
||||||
out += "backgroundMode";
|
out += "backgroundMode";
|
||||||
|
@ -2479,6 +2490,9 @@ QByteArray EntityItemProperties::getStaticCertificateJSON() const {
|
||||||
// of the entity as reviewed during the certification submission.
|
// of the entity as reviewed during the certification submission.
|
||||||
|
|
||||||
QJsonObject json;
|
QJsonObject json;
|
||||||
|
|
||||||
|
quint32 staticCertificateVersion = getStaticCertificateVersion();
|
||||||
|
|
||||||
if (!getAnimation().getURL().isEmpty()) {
|
if (!getAnimation().getURL().isEmpty()) {
|
||||||
json["animationURL"] = getAnimation().getURL();
|
json["animationURL"] = getAnimation().getURL();
|
||||||
}
|
}
|
||||||
|
@ -2495,7 +2509,11 @@ QByteArray EntityItemProperties::getStaticCertificateJSON() const {
|
||||||
ADD_STRING_PROPERTY(marketplaceID, MarketplaceID);
|
ADD_STRING_PROPERTY(marketplaceID, MarketplaceID);
|
||||||
ADD_STRING_PROPERTY(modelURL, ModelURL);
|
ADD_STRING_PROPERTY(modelURL, ModelURL);
|
||||||
ADD_STRING_PROPERTY(script, Script);
|
ADD_STRING_PROPERTY(script, Script);
|
||||||
|
if (staticCertificateVersion >= 1) {
|
||||||
|
ADD_STRING_PROPERTY(serverScripts, ServerScripts);
|
||||||
|
}
|
||||||
ADD_ENUM_PROPERTY(shapeType, ShapeType);
|
ADD_ENUM_PROPERTY(shapeType, ShapeType);
|
||||||
|
ADD_INT_PROPERTY(staticCertificateVersion, StaticCertificateVersion);
|
||||||
json["type"] = EntityTypes::getEntityTypeName(getType());
|
json["type"] = EntityTypes::getEntityTypeName(getType());
|
||||||
|
|
||||||
return QJsonDocument(json).toJson(QJsonDocument::Compact);
|
return QJsonDocument(json).toJson(QJsonDocument::Compact);
|
||||||
|
|
|
@ -220,6 +220,7 @@ public:
|
||||||
DEFINE_PROPERTY_REF(PROP_EDITION_NUMBER, EditionNumber, editionNumber, quint32, ENTITY_ITEM_DEFAULT_EDITION_NUMBER);
|
DEFINE_PROPERTY_REF(PROP_EDITION_NUMBER, EditionNumber, editionNumber, quint32, ENTITY_ITEM_DEFAULT_EDITION_NUMBER);
|
||||||
DEFINE_PROPERTY_REF(PROP_ENTITY_INSTANCE_NUMBER, EntityInstanceNumber, entityInstanceNumber, quint32, ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER);
|
DEFINE_PROPERTY_REF(PROP_ENTITY_INSTANCE_NUMBER, EntityInstanceNumber, entityInstanceNumber, quint32, ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER);
|
||||||
DEFINE_PROPERTY_REF(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString, ENTITY_ITEM_DEFAULT_CERTIFICATE_ID);
|
DEFINE_PROPERTY_REF(PROP_CERTIFICATE_ID, CertificateID, certificateID, QString, ENTITY_ITEM_DEFAULT_CERTIFICATE_ID);
|
||||||
|
DEFINE_PROPERTY_REF(PROP_STATIC_CERTIFICATE_VERSION, StaticCertificateVersion, staticCertificateVersion, quint32, ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION);
|
||||||
|
|
||||||
// these are used when bouncing location data into and out of scripts
|
// these are used when bouncing location data into and out of scripts
|
||||||
DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3);
|
DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3);
|
||||||
|
@ -473,6 +474,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
|
||||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EditionNumber, editionNumber, "");
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EditionNumber, editionNumber, "");
|
||||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityInstanceNumber, entityInstanceNumber, "");
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, EntityInstanceNumber, entityInstanceNumber, "");
|
||||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CertificateID, certificateID, "");
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CertificateID, certificateID, "");
|
||||||
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, StaticCertificateVersion, staticCertificateVersion, "");
|
||||||
|
|
||||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, "");
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, "");
|
||||||
DEBUG_PROPERTY_IF_CHANGED(debug, properties, HazeMode, hazeMode, "");
|
DEBUG_PROPERTY_IF_CHANGED(debug, properties, HazeMode, hazeMode, "");
|
||||||
|
|
|
@ -41,6 +41,7 @@ const QString ENTITY_ITEM_DEFAULT_MARKETPLACE_ID = QString("");
|
||||||
const quint32 ENTITY_ITEM_DEFAULT_EDITION_NUMBER = 0;
|
const quint32 ENTITY_ITEM_DEFAULT_EDITION_NUMBER = 0;
|
||||||
const quint32 ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER = 0;
|
const quint32 ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER = 0;
|
||||||
const QString ENTITY_ITEM_DEFAULT_CERTIFICATE_ID = QString("");
|
const QString ENTITY_ITEM_DEFAULT_CERTIFICATE_ID = QString("");
|
||||||
|
const quint32 ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION = 0;
|
||||||
|
|
||||||
const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f;
|
const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f;
|
||||||
const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f;
|
const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f;
|
||||||
|
|
|
@ -200,6 +200,7 @@ enum EntityPropertyList {
|
||||||
PROP_EDITION_NUMBER,
|
PROP_EDITION_NUMBER,
|
||||||
PROP_ENTITY_INSTANCE_NUMBER,
|
PROP_ENTITY_INSTANCE_NUMBER,
|
||||||
PROP_CERTIFICATE_ID,
|
PROP_CERTIFICATE_ID,
|
||||||
|
PROP_STATIC_CERTIFICATE_VERSION,
|
||||||
|
|
||||||
PROP_HAZE_MODE,
|
PROP_HAZE_MODE,
|
||||||
|
|
||||||
|
|
|
@ -1821,6 +1821,19 @@ glm::mat4 EntityScriptingInterface::getEntityLocalTransform(const QUuid& entityI
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString EntityScriptingInterface::getStaticCertificateJSON(const QUuid& entityID) {
|
||||||
|
QByteArray result;
|
||||||
|
if (_entityTree) {
|
||||||
|
_entityTree->withReadLock([&] {
|
||||||
|
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
|
||||||
|
if (entity) {
|
||||||
|
result = entity->getProperties().getStaticCertificateJSON();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool EntityScriptingInterface::verifyStaticCertificateProperties(const QUuid& entityID) {
|
bool EntityScriptingInterface::verifyStaticCertificateProperties(const QUuid& entityID) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
if (_entityTree) {
|
if (_entityTree) {
|
||||||
|
|
|
@ -423,6 +423,12 @@ public slots:
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID);
|
Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID);
|
||||||
|
|
||||||
|
|
||||||
|
/**jsdoc
|
||||||
|
* Return the Static Certificate JSON for the specified {EntityID}.
|
||||||
|
* @return {QByteArray} The Static Certificate JSON for the specified entity.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE QString getStaticCertificateJSON(const QUuid& entityID);
|
||||||
Q_INVOKABLE bool verifyStaticCertificateProperties(const QUuid& entityID);
|
Q_INVOKABLE bool verifyStaticCertificateProperties(const QUuid& entityID);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
@ -29,7 +29,6 @@ void EntitySimulation::setEntityTree(EntityTreePointer tree) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::updateEntities() {
|
void EntitySimulation::updateEntities() {
|
||||||
PROFILE_RANGE(simulation_physics, "ES::updateEntities");
|
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
quint64 now = usecTimestampNow();
|
quint64 now = usecTimestampNow();
|
||||||
|
|
||||||
|
@ -38,12 +37,7 @@ void EntitySimulation::updateEntities() {
|
||||||
callUpdateOnEntitiesThatNeedIt(now);
|
callUpdateOnEntitiesThatNeedIt(now);
|
||||||
moveSimpleKinematics(now);
|
moveSimpleKinematics(now);
|
||||||
updateEntitiesInternal(now);
|
updateEntitiesInternal(now);
|
||||||
|
sortEntitiesThatMoved();
|
||||||
{
|
|
||||||
PROFILE_RANGE(simulation_physics, "Sort");
|
|
||||||
PerformanceTimer perfTimer("sortingEntities");
|
|
||||||
sortEntitiesThatMoved();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
void EntitySimulation::takeEntitiesToDelete(VectorOfEntities& entitiesToDelete) {
|
||||||
|
@ -101,6 +95,7 @@ void EntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||||
// protected
|
// protected
|
||||||
void EntitySimulation::expireMortalEntities(const quint64& now) {
|
void EntitySimulation::expireMortalEntities(const quint64& now) {
|
||||||
if (now > _nextExpiry) {
|
if (now > _nextExpiry) {
|
||||||
|
PROFILE_RANGE_EX(simulation_physics, "ExpireMortals", 0xffff00ff, (uint64_t)_mortalEntities.size());
|
||||||
// only search for expired entities if we expect to find one
|
// only search for expired entities if we expect to find one
|
||||||
_nextExpiry = quint64(-1);
|
_nextExpiry = quint64(-1);
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
|
@ -146,6 +141,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) {
|
||||||
|
|
||||||
// protected
|
// protected
|
||||||
void EntitySimulation::sortEntitiesThatMoved() {
|
void EntitySimulation::sortEntitiesThatMoved() {
|
||||||
|
PROFILE_RANGE_EX(simulation_physics, "SortTree", 0xffff00ff, (uint64_t)_entitiesToSort.size());
|
||||||
// NOTE: this is only for entities that have been moved by THIS EntitySimulation.
|
// NOTE: this is only for entities that have been moved by THIS EntitySimulation.
|
||||||
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
|
// External changes to entity position/shape are expected to be sorted outside of the EntitySimulation.
|
||||||
MovingEntitiesOperator moveOperator;
|
MovingEntitiesOperator moveOperator;
|
||||||
|
@ -265,7 +261,7 @@ void EntitySimulation::clearEntities() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
void EntitySimulation::moveSimpleKinematics(const quint64& now) {
|
||||||
PROFILE_RANGE_EX(simulation_physics, "Kinematics", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size());
|
PROFILE_RANGE_EX(simulation_physics, "MoveSimples", 0xffff00ff, (uint64_t)_simpleKinematicEntities.size());
|
||||||
SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin();
|
SetOfEntities::iterator itemItr = _simpleKinematicEntities.begin();
|
||||||
while (itemItr != _simpleKinematicEntities.end()) {
|
while (itemItr != _simpleKinematicEntities.end()) {
|
||||||
EntityItemPointer entity = *itemItr;
|
EntityItemPointer entity = *itemItr;
|
||||||
|
|
|
@ -1770,24 +1770,26 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTree::update(bool simulate) {
|
void EntityTree::update(bool simulate) {
|
||||||
PROFILE_RANGE(simulation_physics, "ET::update");
|
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
||||||
fixupNeedsParentFixups();
|
fixupNeedsParentFixups();
|
||||||
if (simulate && _simulation) {
|
if (simulate && _simulation) {
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
_simulation->updateEntities();
|
_simulation->updateEntities();
|
||||||
VectorOfEntities pendingDeletes;
|
{
|
||||||
_simulation->takeEntitiesToDelete(pendingDeletes);
|
PROFILE_RANGE(simulation_physics, "Deletes");
|
||||||
|
VectorOfEntities pendingDeletes;
|
||||||
|
_simulation->takeEntitiesToDelete(pendingDeletes);
|
||||||
|
if (pendingDeletes.size() > 0) {
|
||||||
|
// translate into list of ID's
|
||||||
|
QSet<EntityItemID> idsToDelete;
|
||||||
|
|
||||||
if (pendingDeletes.size() > 0) {
|
for (auto entity : pendingDeletes) {
|
||||||
// translate into list of ID's
|
idsToDelete.insert(entity->getEntityItemID());
|
||||||
QSet<EntityItemID> idsToDelete;
|
}
|
||||||
|
|
||||||
for (auto entity : pendingDeletes) {
|
// delete these things the roundabout way
|
||||||
idsToDelete.insert(entity->getEntityItemID());
|
deleteEntities(idsToDelete, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete these things the roundabout way
|
|
||||||
deleteEntities(idsToDelete, true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -981,7 +981,7 @@ public:
|
||||||
static QImage extractEquirectangularFace(const QImage& source, gpu::Texture::CubeFace face, int faceWidth) {
|
static QImage extractEquirectangularFace(const QImage& source, gpu::Texture::CubeFace face, int faceWidth) {
|
||||||
QImage image(faceWidth, faceWidth, source.format());
|
QImage image(faceWidth, faceWidth, source.format());
|
||||||
|
|
||||||
glm::vec2 dstInvSize(1.0f / (float)source.width(), 1.0f / (float)source.height());
|
glm::vec2 dstInvSize(1.0f / faceWidth);
|
||||||
|
|
||||||
struct CubeToXYZ {
|
struct CubeToXYZ {
|
||||||
gpu::Texture::CubeFace _face;
|
gpu::Texture::CubeFace _face;
|
||||||
|
|
|
@ -127,7 +127,11 @@ void Assignment::swap(Assignment& otherAssignment) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Assignment::getTypeName() const {
|
const char* Assignment::getTypeName() const {
|
||||||
switch (_type) {
|
return typeToString(_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Assignment::typeToString(Assignment::Type type) {
|
||||||
|
switch (type) {
|
||||||
case Assignment::AudioMixerType:
|
case Assignment::AudioMixerType:
|
||||||
return "audio-mixer";
|
return "audio-mixer";
|
||||||
case Assignment::AvatarMixerType:
|
case Assignment::AvatarMixerType:
|
||||||
|
|
|
@ -28,6 +28,7 @@ class Assignment : public QObject {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
enum Type : uint8_t {
|
enum Type : uint8_t {
|
||||||
|
FirstType = 0,
|
||||||
AudioMixerType = 0,
|
AudioMixerType = 0,
|
||||||
AvatarMixerType = 1,
|
AvatarMixerType = 1,
|
||||||
AgentType = 2,
|
AgentType = 2,
|
||||||
|
@ -89,6 +90,7 @@ public:
|
||||||
const QString& getNodeVersion() const { return _nodeVersion; }
|
const QString& getNodeVersion() const { return _nodeVersion; }
|
||||||
|
|
||||||
const char* getTypeName() const;
|
const char* getTypeName() const;
|
||||||
|
static const char* typeToString(Assignment::Type type);
|
||||||
|
|
||||||
friend QDebug operator<<(QDebug debug, const Assignment& assignment);
|
friend QDebug operator<<(QDebug debug, const Assignment& assignment);
|
||||||
friend QDataStream& operator<<(QDataStream &out, const Assignment& assignment);
|
friend QDataStream& operator<<(QDataStream &out, const Assignment& assignment);
|
||||||
|
|
|
@ -141,3 +141,16 @@ void UserActivityLoggerScriptingInterface::commerceWalletSetupFinished(int times
|
||||||
payload["secondsToComplete"] = secondsToComplete;
|
payload["secondsToComplete"] = secondsToComplete;
|
||||||
doLogAction("commerceWalletSetupFinished", payload);
|
doLogAction("commerceWalletSetupFinished", payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UserActivityLoggerScriptingInterface::commercePassphraseEntry(QString source) {
|
||||||
|
QJsonObject payload;
|
||||||
|
payload["source"] = source;
|
||||||
|
doLogAction("commercePassphraseEntry", payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserActivityLoggerScriptingInterface::commercePassphraseAuthenticationStatus(QString status) {
|
||||||
|
QJsonObject payload;
|
||||||
|
payload["status"] = status;
|
||||||
|
doLogAction("commercePassphraseAuthenticationStatus", payload);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
Q_INVOKABLE void commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain);
|
Q_INVOKABLE void commerceWalletSetupStarted(int timestamp, QString setupAttemptID, int setupFlowVersion, QString referrer, QString currentDomain);
|
||||||
Q_INVOKABLE void commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName);
|
Q_INVOKABLE void commerceWalletSetupProgress(int timestamp, QString setupAttemptID, int secondsElapsed, int currentStepNumber, QString currentStepName);
|
||||||
Q_INVOKABLE void commerceWalletSetupFinished(int timestamp, QString setupAttemptID, int secondsToComplete);
|
Q_INVOKABLE void commerceWalletSetupFinished(int timestamp, QString setupAttemptID, int secondsToComplete);
|
||||||
|
Q_INVOKABLE void commercePassphraseEntry(QString source);
|
||||||
|
Q_INVOKABLE void commercePassphraseAuthenticationStatus(QString status);
|
||||||
private:
|
private:
|
||||||
void doLogAction(QString action, QJsonObject details = {});
|
void doLogAction(QString action, QJsonObject details = {});
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include "Connection.h"
|
#include "Connection.h"
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
@ -60,6 +62,15 @@ Connection::Connection(Socket* parentSocket, HifiSockAddr destination, std::uniq
|
||||||
_ack2Packet = ControlPacket::create(ControlPacket::ACK2, ACK2_PAYLOAD_BYTES);
|
_ack2Packet = ControlPacket::create(ControlPacket::ACK2, ACK2_PAYLOAD_BYTES);
|
||||||
_lossReport = ControlPacket::create(ControlPacket::NAK, NAK_PACKET_PAYLOAD_BYTES);
|
_lossReport = ControlPacket::create(ControlPacket::NAK, NAK_PACKET_PAYLOAD_BYTES);
|
||||||
_handshakeACK = ControlPacket::create(ControlPacket::HandshakeACK, HANDSHAKE_ACK_PAYLOAD_BYTES);
|
_handshakeACK = ControlPacket::create(ControlPacket::HandshakeACK, HANDSHAKE_ACK_PAYLOAD_BYTES);
|
||||||
|
|
||||||
|
|
||||||
|
// setup psuedo-random number generation shared by all connections
|
||||||
|
static std::random_device rd;
|
||||||
|
static std::mt19937 generator(rd());
|
||||||
|
static std::uniform_int_distribution<> distribution(0, SequenceNumber::MAX);
|
||||||
|
|
||||||
|
// randomize the intial sequence number
|
||||||
|
_initialSequenceNumber = SequenceNumber(distribution(generator));
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection::~Connection() {
|
Connection::~Connection() {
|
||||||
|
@ -79,11 +90,11 @@ void Connection::stopSendQueue() {
|
||||||
// tell the send queue to stop and be deleted
|
// tell the send queue to stop and be deleted
|
||||||
|
|
||||||
sendQueue->stop();
|
sendQueue->stop();
|
||||||
|
|
||||||
|
_lastMessageNumber = sendQueue->getCurrentMessageNumber();
|
||||||
|
|
||||||
sendQueue->deleteLater();
|
sendQueue->deleteLater();
|
||||||
|
|
||||||
// since we're stopping the send queue we should consider our handshake ACK not receieved
|
|
||||||
_hasReceivedHandshakeACK = false;
|
|
||||||
|
|
||||||
// wait on the send queue thread so we know the send queue is gone
|
// wait on the send queue thread so we know the send queue is gone
|
||||||
sendQueueThread->quit();
|
sendQueueThread->quit();
|
||||||
sendQueueThread->wait();
|
sendQueueThread->wait();
|
||||||
|
@ -101,13 +112,19 @@ void Connection::setMaxBandwidth(int maxBandwidth) {
|
||||||
|
|
||||||
SendQueue& Connection::getSendQueue() {
|
SendQueue& Connection::getSendQueue() {
|
||||||
if (!_sendQueue) {
|
if (!_sendQueue) {
|
||||||
|
|
||||||
// we may have a sequence number from the previous inactive queue - re-use that so that the
|
// we may have a sequence number from the previous inactive queue - re-use that so that the
|
||||||
// receiver is getting the sequence numbers it expects (given that the connection must still be active)
|
// receiver is getting the sequence numbers it expects (given that the connection must still be active)
|
||||||
|
|
||||||
// Lasily create send queue
|
// Lasily create send queue
|
||||||
_sendQueue = SendQueue::create(_parentSocket, _destination);
|
|
||||||
_lastReceivedACK = _sendQueue->getCurrentSequenceNumber();
|
if (!_hasReceivedHandshakeACK) {
|
||||||
|
// First time creating a send queue for this connection
|
||||||
|
_sendQueue = SendQueue::create(_parentSocket, _destination, _initialSequenceNumber - 1, _lastMessageNumber, _hasReceivedHandshakeACK);
|
||||||
|
_lastReceivedACK = _sendQueue->getCurrentSequenceNumber();
|
||||||
|
} else {
|
||||||
|
// Connection already has a handshake from a previous send queue
|
||||||
|
_sendQueue = SendQueue::create(_parentSocket, _destination, _lastReceivedACK, _lastMessageNumber, _hasReceivedHandshakeACK);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef UDT_CONNECTION_DEBUG
|
#ifdef UDT_CONNECTION_DEBUG
|
||||||
qCDebug(networking) << "Created SendQueue for connection to" << _destination;
|
qCDebug(networking) << "Created SendQueue for connection to" << _destination;
|
||||||
|
@ -142,14 +159,6 @@ void Connection::queueInactive() {
|
||||||
#ifdef UDT_CONNECTION_DEBUG
|
#ifdef UDT_CONNECTION_DEBUG
|
||||||
qCDebug(networking) << "Connection to" << _destination << "has stopped its SendQueue.";
|
qCDebug(networking) << "Connection to" << _destination << "has stopped its SendQueue.";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!_hasReceivedHandshake || !_isReceivingData) {
|
|
||||||
#ifdef UDT_CONNECTION_DEBUG
|
|
||||||
qCDebug(networking) << "Connection SendQueue to" << _destination << "stopped and no data is being received - stopping connection.";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
deactivate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::queueTimeout() {
|
void Connection::queueTimeout() {
|
||||||
|
@ -184,11 +193,16 @@ void Connection::queueReceivedMessagePacket(std::unique_ptr<Packet> packet) {
|
||||||
|
|
||||||
while (pendingMessage.hasAvailablePackets()) {
|
while (pendingMessage.hasAvailablePackets()) {
|
||||||
auto packet = pendingMessage.removeNextPacket();
|
auto packet = pendingMessage.removeNextPacket();
|
||||||
_parentSocket->messageReceived(std::move(packet));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pendingMessage.isComplete()) {
|
auto packetPosition = packet->getPacketPosition();
|
||||||
_pendingReceivedMessages.erase(messageNumber);
|
|
||||||
|
_parentSocket->messageReceived(std::move(packet));
|
||||||
|
|
||||||
|
// if this was the last or only packet, then we can remove the pending message from our hash
|
||||||
|
if (packetPosition == Packet::PacketPosition::LAST ||
|
||||||
|
packetPosition == Packet::PacketPosition::ONLY) {
|
||||||
|
_pendingReceivedMessages.erase(messageNumber);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,19 +222,6 @@ void Connection::sync() {
|
||||||
&& duration_cast<seconds>(sincePacketReceive).count() >= MIN_SECONDS_BEFORE_EXPIRY ) {
|
&& duration_cast<seconds>(sincePacketReceive).count() >= MIN_SECONDS_BEFORE_EXPIRY ) {
|
||||||
// the receive side of this connection is expired
|
// the receive side of this connection is expired
|
||||||
_isReceivingData = false;
|
_isReceivingData = false;
|
||||||
|
|
||||||
// if we don't have a send queue that means the whole connection has expired and we can emit our signal
|
|
||||||
// otherwise we'll wait for it to also timeout before cleaning up
|
|
||||||
if (!_sendQueue) {
|
|
||||||
|
|
||||||
#ifdef UDT_CONNECTION_DEBUG
|
|
||||||
qCDebug(networking) << "Connection to" << _destination << "no longer receiving any data and there is currently no send queue - stopping connection.";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
deactivate();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset the number of light ACKs or non SYN ACKs during this sync interval
|
// reset the number of light ACKs or non SYN ACKs during this sync interval
|
||||||
|
@ -242,26 +243,6 @@ void Connection::sync() {
|
||||||
sendTimeoutNAK();
|
sendTimeoutNAK();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!_sendQueue) {
|
|
||||||
// we haven't received a packet and we're not sending
|
|
||||||
// this most likely means we were started erroneously
|
|
||||||
// check the start time for this connection and auto expire it after 5 seconds of not receiving or sending any data
|
|
||||||
static const int CONNECTION_NOT_USED_EXPIRY_SECONDS = 5;
|
|
||||||
auto secondsSinceStart = duration_cast<seconds>(p_high_resolution_clock::now() - _connectionStart).count();
|
|
||||||
|
|
||||||
if (secondsSinceStart >= CONNECTION_NOT_USED_EXPIRY_SECONDS) {
|
|
||||||
// it's been CONNECTION_NOT_USED_EXPIRY_SECONDS and nothing has actually happened with this connection
|
|
||||||
// consider it inactive and emit our inactivity signal
|
|
||||||
|
|
||||||
#ifdef UDT_CONNECTION_DEBUG
|
|
||||||
qCDebug(networking) << "Connection to" << _destination << "did not receive or send any data in last"
|
|
||||||
<< CONNECTION_NOT_USED_EXPIRY_SECONDS << "seconds - stopping connection.";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
deactivate();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +425,6 @@ void Connection::sendHandshakeRequest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, int packetSize, int payloadSize) {
|
bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, int packetSize, int payloadSize) {
|
||||||
|
|
||||||
if (!_hasReceivedHandshake) {
|
if (!_hasReceivedHandshake) {
|
||||||
// Refuse to process any packets until we've received the handshake
|
// Refuse to process any packets until we've received the handshake
|
||||||
// Send handshake request to re-request a handshake
|
// Send handshake request to re-request a handshake
|
||||||
|
@ -536,7 +516,7 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in
|
||||||
} else {
|
} else {
|
||||||
_stats.recordReceivedPackets(payloadSize, packetSize);
|
_stats.recordReceivedPackets(payloadSize, packetSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
return !wasDuplicate;
|
return !wasDuplicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -827,11 +807,13 @@ void Connection::processHandshakeACK(ControlPacketPointer controlPacket) {
|
||||||
SequenceNumber initialSequenceNumber;
|
SequenceNumber initialSequenceNumber;
|
||||||
controlPacket->readPrimitive(&initialSequenceNumber);
|
controlPacket->readPrimitive(&initialSequenceNumber);
|
||||||
|
|
||||||
// hand off this handshake ACK to the send queue so it knows it can start sending
|
if (initialSequenceNumber == _initialSequenceNumber) {
|
||||||
getSendQueue().handshakeACK(initialSequenceNumber);
|
// hand off this handshake ACK to the send queue so it knows it can start sending
|
||||||
|
getSendQueue().handshakeACK();
|
||||||
// indicate that handshake ACK was received
|
|
||||||
_hasReceivedHandshakeACK = true;
|
// indicate that handshake ACK was received
|
||||||
|
_hasReceivedHandshakeACK = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,6 @@ class Socket;
|
||||||
class PendingReceivedMessage {
|
class PendingReceivedMessage {
|
||||||
public:
|
public:
|
||||||
void enqueuePacket(std::unique_ptr<Packet> packet);
|
void enqueuePacket(std::unique_ptr<Packet> packet);
|
||||||
bool isComplete() const { return _hasLastPacket && _numPackets == _packets.size(); }
|
|
||||||
bool hasAvailablePackets() const;
|
bool hasAvailablePackets() const;
|
||||||
std::unique_ptr<Packet> removeNextPacket();
|
std::unique_ptr<Packet> removeNextPacket();
|
||||||
|
|
||||||
|
@ -72,8 +71,6 @@ public:
|
||||||
void queueReceivedMessagePacket(std::unique_ptr<Packet> packet);
|
void queueReceivedMessagePacket(std::unique_ptr<Packet> packet);
|
||||||
|
|
||||||
ConnectionStats::Stats sampleStats() { return _stats.sample(); }
|
ConnectionStats::Stats sampleStats() { return _stats.sample(); }
|
||||||
|
|
||||||
bool isActive() const { return _isActive; }
|
|
||||||
|
|
||||||
HifiSockAddr getDestination() const { return _destination; }
|
HifiSockAddr getDestination() const { return _destination; }
|
||||||
|
|
||||||
|
@ -83,7 +80,6 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void packetSent();
|
void packetSent();
|
||||||
void connectionInactive(const HifiSockAddr& sockAddr);
|
|
||||||
void receiverHandshakeRequestComplete(const HifiSockAddr& sockAddr);
|
void receiverHandshakeRequestComplete(const HifiSockAddr& sockAddr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
@ -112,8 +108,6 @@ private:
|
||||||
void resetReceiveState();
|
void resetReceiveState();
|
||||||
void resetRTT();
|
void resetRTT();
|
||||||
|
|
||||||
void deactivate() { _isActive = false; emit connectionInactive(_destination); }
|
|
||||||
|
|
||||||
SendQueue& getSendQueue();
|
SendQueue& getSendQueue();
|
||||||
SequenceNumber nextACK() const;
|
SequenceNumber nextACK() const;
|
||||||
void updateRTT(int rtt);
|
void updateRTT(int rtt);
|
||||||
|
@ -138,9 +132,11 @@ private:
|
||||||
p_high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender
|
p_high_resolution_clock::time_point _lastReceiveTime; // holds the last time we received anything from sender
|
||||||
|
|
||||||
bool _isReceivingData { false }; // flag used for expiry of receipt portion of connection
|
bool _isReceivingData { false }; // flag used for expiry of receipt portion of connection
|
||||||
bool _isActive { true }; // flag used for inactivity of connection
|
|
||||||
|
|
||||||
SequenceNumber _initialReceiveSequenceNumber; // Randomized by peer SendQueue on creation, identifies connection during re-connect requests
|
SequenceNumber _initialSequenceNumber; // Randomized on Connection creation, identifies connection during re-connect requests
|
||||||
|
SequenceNumber _initialReceiveSequenceNumber; // Randomized by peer Connection on creation, identifies connection during re-connect requests
|
||||||
|
|
||||||
|
MessageNumber _lastMessageNumber { 0 };
|
||||||
|
|
||||||
LossList _lossList; // List of all missing packets
|
LossList _lossList; // List of all missing packets
|
||||||
SequenceNumber _lastReceivedSequenceNumber; // The largest sequence number received from the peer
|
SequenceNumber _lastReceivedSequenceNumber; // The largest sequence number received from the peer
|
||||||
|
|
|
@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::EntityEdit:
|
case PacketType::EntityEdit:
|
||||||
case PacketType::EntityData:
|
case PacketType::EntityData:
|
||||||
case PacketType::EntityPhysics:
|
case PacketType::EntityPhysics:
|
||||||
return static_cast<PacketVersion>(EntityVersion::HazeEffect);
|
return static_cast<PacketVersion>(EntityVersion::StaticCertJsonVersionOne);
|
||||||
|
|
||||||
case PacketType::EntityQuery:
|
case PacketType::EntityQuery:
|
||||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConnectionIdentifier);
|
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConnectionIdentifier);
|
||||||
|
|
|
@ -199,7 +199,8 @@ QDebug operator<<(QDebug debug, const PacketType& type);
|
||||||
enum class EntityVersion : PacketVersion {
|
enum class EntityVersion : PacketVersion {
|
||||||
StrokeColorProperty = 77,
|
StrokeColorProperty = 77,
|
||||||
HasDynamicOwnershipTests,
|
HasDynamicOwnershipTests,
|
||||||
HazeEffect
|
HazeEffect,
|
||||||
|
StaticCertJsonVersionOne
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
using namespace udt;
|
using namespace udt;
|
||||||
|
|
||||||
PacketQueue::PacketQueue() {
|
PacketQueue::PacketQueue(MessageNumber messageNumber) : _currentMessageNumber(messageNumber) {
|
||||||
_channels.emplace_back(new std::list<PacketPointer>());
|
_channels.emplace_back(new std::list<PacketPointer>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ class PacketQueue {
|
||||||
using Channels = std::vector<Channel>;
|
using Channels = std::vector<Channel>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PacketQueue();
|
PacketQueue(MessageNumber messageNumber = 0);
|
||||||
void queuePacket(PacketPointer packet);
|
void queuePacket(PacketPointer packet);
|
||||||
void queuePacketList(PacketListPointer packetList);
|
void queuePacketList(PacketListPointer packetList);
|
||||||
|
|
||||||
|
@ -42,6 +42,8 @@ public:
|
||||||
PacketPointer takePacket();
|
PacketPointer takePacket();
|
||||||
|
|
||||||
Mutex& getLock() { return _packetsLock; }
|
Mutex& getLock() { return _packetsLock; }
|
||||||
|
|
||||||
|
MessageNumber getCurrentMessageNumber() const { return _currentMessageNumber; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MessageNumber getNextMessageNumber();
|
MessageNumber getNextMessageNumber();
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include "SendQueue.h"
|
#include "SendQueue.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <random>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
|
@ -62,10 +61,12 @@ private:
|
||||||
Mutex2& _mutex2;
|
Mutex2& _mutex2;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destination) {
|
std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destination, SequenceNumber currentSequenceNumber,
|
||||||
|
MessageNumber currentMessageNumber, bool hasReceivedHandshakeACK) {
|
||||||
Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*");
|
Q_ASSERT_X(socket, "SendQueue::create", "Must be called with a valid Socket*");
|
||||||
|
|
||||||
auto queue = std::unique_ptr<SendQueue>(new SendQueue(socket, destination));
|
auto queue = std::unique_ptr<SendQueue>(new SendQueue(socket, destination, currentSequenceNumber,
|
||||||
|
currentMessageNumber, hasReceivedHandshakeACK));
|
||||||
|
|
||||||
// Setup queue private thread
|
// Setup queue private thread
|
||||||
QThread* thread = new QThread;
|
QThread* thread = new QThread;
|
||||||
|
@ -84,25 +85,18 @@ std::unique_ptr<SendQueue> SendQueue::create(Socket* socket, HifiSockAddr destin
|
||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SendQueue::SendQueue(Socket* socket, HifiSockAddr dest) :
|
SendQueue::SendQueue(Socket* socket, HifiSockAddr dest, SequenceNumber currentSequenceNumber,
|
||||||
|
MessageNumber currentMessageNumber, bool hasReceivedHandshakeACK) :
|
||||||
|
_packets(currentMessageNumber),
|
||||||
_socket(socket),
|
_socket(socket),
|
||||||
_destination(dest)
|
_destination(dest)
|
||||||
{
|
{
|
||||||
// setup psuedo-random number generation for all instances of SendQueue
|
// set our member variables from current sequence number
|
||||||
static std::random_device rd;
|
_currentSequenceNumber = currentSequenceNumber;
|
||||||
static std::mt19937 generator(rd());
|
|
||||||
static std::uniform_int_distribution<> distribution(0, SequenceNumber::MAX);
|
|
||||||
|
|
||||||
// randomize the intial sequence number
|
|
||||||
_initialSequenceNumber = SequenceNumber(distribution(generator));
|
|
||||||
|
|
||||||
// set our member variables from randomized initial number
|
|
||||||
_currentSequenceNumber = _initialSequenceNumber - 1;
|
|
||||||
_atomicCurrentSequenceNumber = uint32_t(_currentSequenceNumber);
|
_atomicCurrentSequenceNumber = uint32_t(_currentSequenceNumber);
|
||||||
_lastACKSequenceNumber = uint32_t(_currentSequenceNumber) - 1;
|
_lastACKSequenceNumber = uint32_t(_currentSequenceNumber);
|
||||||
|
|
||||||
// default the last receiver response to the current time
|
_hasReceivedHandshakeACK = hasReceivedHandshakeACK;
|
||||||
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SendQueue::~SendQueue() {
|
SendQueue::~SendQueue() {
|
||||||
|
@ -114,8 +108,8 @@ void SendQueue::queuePacket(std::unique_ptr<Packet> packet) {
|
||||||
// call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets
|
// call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets
|
||||||
_emptyCondition.notify_one();
|
_emptyCondition.notify_one();
|
||||||
|
|
||||||
if (!this->thread()->isRunning() && _state == State::NotStarted) {
|
if (!thread()->isRunning() && _state == State::NotStarted) {
|
||||||
this->thread()->start();
|
thread()->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,8 +119,8 @@ void SendQueue::queuePacketList(std::unique_ptr<PacketList> packetList) {
|
||||||
// call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets
|
// call notify_one on the condition_variable_any in case the send thread is sleeping waiting for packets
|
||||||
_emptyCondition.notify_one();
|
_emptyCondition.notify_one();
|
||||||
|
|
||||||
if (!this->thread()->isRunning() && _state == State::NotStarted) {
|
if (!thread()->isRunning() && _state == State::NotStarted) {
|
||||||
this->thread()->start();
|
thread()->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,9 +138,6 @@ int SendQueue::sendPacket(const Packet& packet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::ack(SequenceNumber ack) {
|
void SendQueue::ack(SequenceNumber ack) {
|
||||||
// this is a response from the client, re-set our timeout expiry and our last response time
|
|
||||||
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
|
||||||
|
|
||||||
if (_lastACKSequenceNumber == (uint32_t) ack) {
|
if (_lastACKSequenceNumber == (uint32_t) ack) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -173,10 +164,7 @@ void SendQueue::ack(SequenceNumber ack) {
|
||||||
_emptyCondition.notify_one();
|
_emptyCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
||||||
// this is a response from the client, re-set our timeout expiry
|
|
||||||
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
||||||
_naks.insert(start, end);
|
_naks.insert(start, end);
|
||||||
|
@ -197,9 +185,6 @@ void SendQueue::fastRetransmit(udt::SequenceNumber ack) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
|
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
|
||||||
// this is a response from the client, re-set our timeout expiry
|
|
||||||
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
||||||
_naks.clear();
|
_naks.clear();
|
||||||
|
@ -225,8 +210,11 @@ void SendQueue::sendHandshake() {
|
||||||
std::unique_lock<std::mutex> handshakeLock { _handshakeMutex };
|
std::unique_lock<std::mutex> handshakeLock { _handshakeMutex };
|
||||||
if (!_hasReceivedHandshakeACK) {
|
if (!_hasReceivedHandshakeACK) {
|
||||||
// we haven't received a handshake ACK from the client, send another now
|
// we haven't received a handshake ACK from the client, send another now
|
||||||
|
// if the handshake hasn't been completed, then the initial sequence number
|
||||||
|
// should be the current sequence number + 1
|
||||||
|
SequenceNumber initialSequenceNumber = _currentSequenceNumber + 1;
|
||||||
auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, sizeof(SequenceNumber));
|
auto handshakePacket = ControlPacket::create(ControlPacket::Handshake, sizeof(SequenceNumber));
|
||||||
handshakePacket->writePrimitive(_initialSequenceNumber);
|
handshakePacket->writePrimitive(initialSequenceNumber);
|
||||||
_socket->writeBasePacket(*handshakePacket, _destination);
|
_socket->writeBasePacket(*handshakePacket, _destination);
|
||||||
|
|
||||||
// we wait for the ACK or the re-send interval to expire
|
// we wait for the ACK or the re-send interval to expire
|
||||||
|
@ -235,18 +223,14 @@ void SendQueue::sendHandshake() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SendQueue::handshakeACK(SequenceNumber initialSequenceNumber) {
|
void SendQueue::handshakeACK() {
|
||||||
if (initialSequenceNumber == _initialSequenceNumber) {
|
{
|
||||||
{
|
std::lock_guard<std::mutex> locker { _handshakeMutex };
|
||||||
std::lock_guard<std::mutex> locker { _handshakeMutex };
|
_hasReceivedHandshakeACK = true;
|
||||||
_hasReceivedHandshakeACK = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
|
||||||
|
|
||||||
// Notify on the handshake ACK condition
|
|
||||||
_handshakeACKCondition.notify_one();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify on the handshake ACK condition
|
||||||
|
_handshakeACKCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
SequenceNumber SendQueue::getNextSequenceNumber() {
|
SequenceNumber SendQueue::getNextSequenceNumber() {
|
||||||
|
@ -540,28 +524,6 @@ bool SendQueue::maybeResendPacket() {
|
||||||
bool SendQueue::isInactive(bool attemptedToSendPacket) {
|
bool SendQueue::isInactive(bool attemptedToSendPacket) {
|
||||||
// check for connection timeout first
|
// check for connection timeout first
|
||||||
|
|
||||||
// that will be the case if we have had 16 timeouts since hearing back from the client, and it has been
|
|
||||||
// at least 5 seconds
|
|
||||||
static const int NUM_TIMEOUTS_BEFORE_INACTIVE = 16;
|
|
||||||
static const int MIN_MS_BEFORE_INACTIVE = 5 * 1000;
|
|
||||||
|
|
||||||
auto sinceLastResponse = (QDateTime::currentMSecsSinceEpoch() - _lastReceiverResponse);
|
|
||||||
|
|
||||||
if (sinceLastResponse > 0 &&
|
|
||||||
sinceLastResponse >= int64_t(NUM_TIMEOUTS_BEFORE_INACTIVE * (_estimatedTimeout / USECS_PER_MSEC)) &&
|
|
||||||
sinceLastResponse > MIN_MS_BEFORE_INACTIVE) {
|
|
||||||
// If the flow window has been full for over CONSIDER_INACTIVE_AFTER,
|
|
||||||
// then signal the queue is inactive and return so it can be cleaned up
|
|
||||||
|
|
||||||
#ifdef UDT_CONNECTION_DEBUG
|
|
||||||
qCDebug(networking) << "SendQueue to" << _destination << "reached" << NUM_TIMEOUTS_BEFORE_INACTIVE << "timeouts"
|
|
||||||
<< "and" << MIN_MS_BEFORE_INACTIVE << "milliseconds before receiving any ACK/NAK and is now inactive. Stopping.";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
deactivate();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!attemptedToSendPacket) {
|
if (!attemptedToSendPacket) {
|
||||||
// During our processing above we didn't send any packets
|
// During our processing above we didn't send any packets
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,9 @@ public:
|
||||||
Stopped
|
Stopped
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::unique_ptr<SendQueue> create(Socket* socket, HifiSockAddr destination);
|
static std::unique_ptr<SendQueue> create(Socket* socket, HifiSockAddr destination,
|
||||||
|
SequenceNumber currentSequenceNumber, MessageNumber currentMessageNumber,
|
||||||
|
bool hasReceivedHandshakeACK);
|
||||||
|
|
||||||
virtual ~SendQueue();
|
virtual ~SendQueue();
|
||||||
|
|
||||||
|
@ -58,6 +60,7 @@ public:
|
||||||
void queuePacketList(std::unique_ptr<PacketList> packetList);
|
void queuePacketList(std::unique_ptr<PacketList> packetList);
|
||||||
|
|
||||||
SequenceNumber getCurrentSequenceNumber() const { return SequenceNumber(_atomicCurrentSequenceNumber); }
|
SequenceNumber getCurrentSequenceNumber() const { return SequenceNumber(_atomicCurrentSequenceNumber); }
|
||||||
|
MessageNumber getCurrentMessageNumber() const { return _packets.getCurrentMessageNumber(); }
|
||||||
|
|
||||||
void setFlowWindowSize(int flowWindowSize) { _flowWindowSize = flowWindowSize; }
|
void setFlowWindowSize(int flowWindowSize) { _flowWindowSize = flowWindowSize; }
|
||||||
|
|
||||||
|
@ -76,7 +79,7 @@ public slots:
|
||||||
void nak(SequenceNumber start, SequenceNumber end);
|
void nak(SequenceNumber start, SequenceNumber end);
|
||||||
void fastRetransmit(SequenceNumber ack);
|
void fastRetransmit(SequenceNumber ack);
|
||||||
void overrideNAKListFromPacket(ControlPacket& packet);
|
void overrideNAKListFromPacket(ControlPacket& packet);
|
||||||
void handshakeACK(SequenceNumber initialSequenceNumber);
|
void handshakeACK();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void packetSent(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint);
|
void packetSent(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint);
|
||||||
|
@ -91,7 +94,8 @@ private slots:
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SendQueue(Socket* socket, HifiSockAddr dest);
|
SendQueue(Socket* socket, HifiSockAddr dest, SequenceNumber currentSequenceNumber,
|
||||||
|
MessageNumber currentMessageNumber, bool hasReceivedHandshakeACK);
|
||||||
SendQueue(SendQueue& other) = delete;
|
SendQueue(SendQueue& other) = delete;
|
||||||
SendQueue(SendQueue&& other) = delete;
|
SendQueue(SendQueue&& other) = delete;
|
||||||
|
|
||||||
|
@ -115,8 +119,6 @@ private:
|
||||||
|
|
||||||
Socket* _socket { nullptr }; // Socket to send packet on
|
Socket* _socket { nullptr }; // Socket to send packet on
|
||||||
HifiSockAddr _destination; // Destination addr
|
HifiSockAddr _destination; // Destination addr
|
||||||
|
|
||||||
SequenceNumber _initialSequenceNumber; // Randomized on SendQueue creation, identifies connection during re-connect requests
|
|
||||||
|
|
||||||
std::atomic<uint32_t> _lastACKSequenceNumber { 0 }; // Last ACKed sequence number
|
std::atomic<uint32_t> _lastACKSequenceNumber { 0 }; // Last ACKed sequence number
|
||||||
|
|
||||||
|
@ -128,7 +130,6 @@ private:
|
||||||
|
|
||||||
std::atomic<int> _estimatedTimeout { 0 }; // Estimated timeout, set from CC
|
std::atomic<int> _estimatedTimeout { 0 }; // Estimated timeout, set from CC
|
||||||
std::atomic<int> _syncInterval { udt::DEFAULT_SYN_INTERVAL_USECS }; // Sync interval, set from CC
|
std::atomic<int> _syncInterval { udt::DEFAULT_SYN_INTERVAL_USECS }; // Sync interval, set from CC
|
||||||
std::atomic<int64_t> _lastReceiverResponse { 0 }; // Timestamp for the last time we got new data from the receiver (ACK/NAK)
|
|
||||||
|
|
||||||
std::atomic<int> _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC
|
std::atomic<int> _flowWindowSize { 0 }; // Flow control window size (number of packets that can be on wire) - set from CC
|
||||||
|
|
||||||
|
|
|
@ -257,9 +257,6 @@ Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
|
||||||
congestionControl->setMaxBandwidth(_maxBandwidth);
|
congestionControl->setMaxBandwidth(_maxBandwidth);
|
||||||
auto connection = std::unique_ptr<Connection>(new Connection(this, sockAddr, std::move(congestionControl)));
|
auto connection = std::unique_ptr<Connection>(new Connection(this, sockAddr, std::move(congestionControl)));
|
||||||
|
|
||||||
// we queue the connection to cleanup connection in case it asks for it during its own rate control sync
|
|
||||||
QObject::connect(connection.get(), &Connection::connectionInactive, this, &Socket::cleanupConnection);
|
|
||||||
|
|
||||||
// allow higher-level classes to find out when connections have completed a handshake
|
// allow higher-level classes to find out when connections have completed a handshake
|
||||||
QObject::connect(connection.get(), &Connection::receiverHandshakeRequestComplete,
|
QObject::connect(connection.get(), &Connection::receiverHandshakeRequestComplete,
|
||||||
this, &Socket::clientHandshakeRequestComplete);
|
this, &Socket::clientHandshakeRequestComplete);
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
#include <EntityItem.h>
|
#include <EntityItem.h>
|
||||||
#include <EntityItemProperties.h>
|
#include <EntityItemProperties.h>
|
||||||
#include <EntityEditPacketSender.h>
|
#include <EntityEditPacketSender.h>
|
||||||
#include <PhysicsCollisionGroups.h>
|
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
|
#include <PhysicsCollisionGroups.h>
|
||||||
|
#include <Profile.h>
|
||||||
|
|
||||||
#include "BulletUtil.h"
|
#include "BulletUtil.h"
|
||||||
#include "EntityMotionState.h"
|
#include "EntityMotionState.h"
|
||||||
|
@ -325,6 +326,7 @@ bool EntityMotionState::isCandidateForOwnership() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync");
|
||||||
// NOTE: we only get here if we think we own the simulation
|
// NOTE: we only get here if we think we own the simulation
|
||||||
assert(_body);
|
assert(_body);
|
||||||
|
|
||||||
|
@ -476,6 +478,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend");
|
||||||
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
||||||
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
||||||
assert(_entity);
|
assert(_entity);
|
||||||
|
@ -516,6 +519,7 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "Send");
|
||||||
assert(_entity);
|
assert(_entity);
|
||||||
assert(entityTreeIsLocked());
|
assert(entityTreeIsLocked());
|
||||||
|
|
||||||
|
@ -731,6 +735,7 @@ void EntityMotionState::resetMeasuredBodyAcceleration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::measureBodyAcceleration() {
|
void EntityMotionState::measureBodyAcceleration() {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "MeasureAccel");
|
||||||
// try to manually measure the true acceleration of the object
|
// try to manually measure the true acceleration of the object
|
||||||
uint32_t thisStep = ObjectMotionState::getWorldSimulationStep();
|
uint32_t thisStep = ObjectMotionState::getWorldSimulationStep();
|
||||||
uint32_t numSubsteps = thisStep - _lastMeasureStep;
|
uint32_t numSubsteps = thisStep - _lastMeasureStep;
|
||||||
|
|
|
@ -10,12 +10,14 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include "PhysicalEntitySimulation.h"
|
||||||
|
|
||||||
|
#include <Profile.h>
|
||||||
|
|
||||||
#include "PhysicsHelpers.h"
|
#include "PhysicsHelpers.h"
|
||||||
#include "PhysicsLogging.h"
|
#include "PhysicsLogging.h"
|
||||||
#include "ShapeManager.h"
|
#include "ShapeManager.h"
|
||||||
|
|
||||||
#include "PhysicalEntitySimulation.h"
|
|
||||||
|
|
||||||
PhysicalEntitySimulation::PhysicalEntitySimulation() {
|
PhysicalEntitySimulation::PhysicalEntitySimulation() {
|
||||||
}
|
}
|
||||||
|
@ -274,20 +276,24 @@ void PhysicalEntitySimulation::handleDeactivatedMotionStates(const VectorOfMotio
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) {
|
void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionStates& motionStates) {
|
||||||
|
PROFILE_RANGE_EX(simulation_physics, "ChangedEntities", 0x00000000, (uint64_t)motionStates.size());
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
|
|
||||||
// walk the motionStates looking for those that correspond to entities
|
// walk the motionStates looking for those that correspond to entities
|
||||||
for (auto stateItr : motionStates) {
|
{
|
||||||
ObjectMotionState* state = &(*stateItr);
|
PROFILE_RANGE_EX(simulation_physics, "Filter", 0x00000000, (uint64_t)motionStates.size());
|
||||||
assert(state);
|
for (auto stateItr : motionStates) {
|
||||||
if (state->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
ObjectMotionState* state = &(*stateItr);
|
||||||
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
assert(state);
|
||||||
EntityItemPointer entity = entityState->getEntity();
|
if (state->getType() == MOTIONSTATE_TYPE_ENTITY) {
|
||||||
assert(entity.get());
|
EntityMotionState* entityState = static_cast<EntityMotionState*>(state);
|
||||||
if (entityState->isCandidateForOwnership()) {
|
EntityItemPointer entity = entityState->getEntity();
|
||||||
_outgoingChanges.insert(entityState);
|
assert(entity.get());
|
||||||
|
if (entityState->isCandidateForOwnership()) {
|
||||||
|
_outgoingChanges.insert(entityState);
|
||||||
|
}
|
||||||
|
_entitiesToSort.insert(entity);
|
||||||
}
|
}
|
||||||
_entitiesToSort.insert(entity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,6 +308,7 @@ void PhysicalEntitySimulation::handleChangedMotionStates(const VectorOfMotionSta
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for entities to prune or update
|
// look for entities to prune or update
|
||||||
|
PROFILE_RANGE_EX(simulation_physics, "Prune/Send", 0x00000000, (uint64_t)_outgoingChanges.size());
|
||||||
QSet<EntityMotionState*>::iterator stateItr = _outgoingChanges.begin();
|
QSet<EntityMotionState*>::iterator stateItr = _outgoingChanges.begin();
|
||||||
while (stateItr != _outgoingChanges.end()) {
|
while (stateItr != _outgoingChanges.end()) {
|
||||||
EntityMotionState* state = *stateItr;
|
EntityMotionState* state = *stateItr;
|
||||||
|
|
|
@ -11,7 +11,12 @@
|
||||||
|
|
||||||
#include <PhysicsCollisionGroups.h>
|
#include <PhysicsCollisionGroups.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
#include <Profile.h>
|
||||||
|
|
||||||
#include "CharacterController.h"
|
#include "CharacterController.h"
|
||||||
#include "ObjectMotionState.h"
|
#include "ObjectMotionState.h"
|
||||||
|
@ -290,6 +295,7 @@ void PhysicsEngine::stepSimulation() {
|
||||||
float timeStep = btMin(dt, MAX_TIMESTEP);
|
float timeStep = btMin(dt, MAX_TIMESTEP);
|
||||||
|
|
||||||
if (_myAvatarController) {
|
if (_myAvatarController) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "avatarController");
|
||||||
BT_PROFILE("avatarController");
|
BT_PROFILE("avatarController");
|
||||||
// TODO: move this stuff outside and in front of stepSimulation, because
|
// TODO: move this stuff outside and in front of stepSimulation, because
|
||||||
// the updateShapeIfNecessary() call needs info from MyAvatar and should
|
// the updateShapeIfNecessary() call needs info from MyAvatar and should
|
||||||
|
@ -328,45 +334,107 @@ void PhysicsEngine::stepSimulation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CProfileOperator {
|
||||||
|
public:
|
||||||
|
CProfileOperator() {}
|
||||||
|
void recurse(CProfileIterator* itr, QString context) {
|
||||||
|
// The context string will get too long if we accumulate it properly
|
||||||
|
//QString newContext = context + QString("/") + itr->Get_Current_Parent_Name();
|
||||||
|
// so we use this four-character indentation
|
||||||
|
QString newContext = context + QString(".../");
|
||||||
|
process(itr, newContext);
|
||||||
|
|
||||||
|
// count the children
|
||||||
|
int32_t numChildren = 0;
|
||||||
|
itr->First();
|
||||||
|
while (!itr->Is_Done()) {
|
||||||
|
itr->Next();
|
||||||
|
++numChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse the children
|
||||||
|
if (numChildren > 0) {
|
||||||
|
// recurse the children
|
||||||
|
for (int32_t i = 0; i < numChildren; ++i) {
|
||||||
|
itr->Enter_Child(i);
|
||||||
|
recurse(itr, newContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// retreat back to parent
|
||||||
|
itr->Enter_Parent();
|
||||||
|
}
|
||||||
|
virtual void process(CProfileIterator*, QString context) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StatsHarvester : public CProfileOperator {
|
||||||
|
public:
|
||||||
|
StatsHarvester() {}
|
||||||
|
void process(CProfileIterator* itr, QString context) override {
|
||||||
|
QString name = context + itr->Get_Current_Parent_Name();
|
||||||
|
uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * itr->Get_Current_Parent_Total_Time());
|
||||||
|
PerformanceTimer::addTimerRecord(name, time);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
class StatsWriter : public CProfileOperator {
|
||||||
|
public:
|
||||||
|
StatsWriter(QString filename) : _file(filename) {
|
||||||
|
_file.open(QFile::WriteOnly);
|
||||||
|
if (_file.error() != QFileDevice::NoError) {
|
||||||
|
qCDebug(physics) << "unable to open file " << _file.fileName() << " to save stepSimulation() stats";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~StatsWriter() {
|
||||||
|
_file.close();
|
||||||
|
}
|
||||||
|
void process(CProfileIterator* itr, QString context) override {
|
||||||
|
QString name = context + itr->Get_Current_Parent_Name();
|
||||||
|
float time = (btScalar)MSECS_PER_SECOND * itr->Get_Current_Parent_Total_Time();
|
||||||
|
if (_file.error() == QFileDevice::NoError) {
|
||||||
|
QTextStream s(&_file);
|
||||||
|
s << name << " = " << time << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
QFile _file;
|
||||||
|
};
|
||||||
|
|
||||||
void PhysicsEngine::harvestPerformanceStats() {
|
void PhysicsEngine::harvestPerformanceStats() {
|
||||||
// unfortunately the full context names get too long for our stats presentation format
|
// unfortunately the full context names get too long for our stats presentation format
|
||||||
//QString contextName = PerformanceTimer::getContextName(); // TODO: how to show full context name?
|
//QString contextName = PerformanceTimer::getContextName(); // TODO: how to show full context name?
|
||||||
QString contextName("...");
|
QString contextName("...");
|
||||||
|
|
||||||
CProfileIterator* profileIterator = CProfileManager::Get_Iterator();
|
CProfileIterator* itr = CProfileManager::Get_Iterator();
|
||||||
if (profileIterator) {
|
if (itr) {
|
||||||
// hunt for stepSimulation context
|
// hunt for stepSimulation context
|
||||||
profileIterator->First();
|
itr->First();
|
||||||
for (int32_t childIndex = 0; !profileIterator->Is_Done(); ++childIndex) {
|
for (int32_t childIndex = 0; !itr->Is_Done(); ++childIndex) {
|
||||||
if (QString(profileIterator->Get_Current_Name()) == "stepSimulation") {
|
if (QString(itr->Get_Current_Name()) == "stepSimulation") {
|
||||||
profileIterator->Enter_Child(childIndex);
|
itr->Enter_Child(childIndex);
|
||||||
recursivelyHarvestPerformanceStats(profileIterator, contextName);
|
StatsHarvester harvester;
|
||||||
|
harvester.recurse(itr, "step/");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
profileIterator->Next();
|
itr->Next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName) {
|
void PhysicsEngine::printPerformanceStatsToFile(const QString& filename) {
|
||||||
QString parentContextName = contextName + QString("/") + QString(profileIterator->Get_Current_Parent_Name());
|
CProfileIterator* itr = CProfileManager::Get_Iterator();
|
||||||
// get the stats for the children
|
if (itr) {
|
||||||
int32_t numChildren = 0;
|
// hunt for stepSimulation context
|
||||||
profileIterator->First();
|
itr->First();
|
||||||
while (!profileIterator->Is_Done()) {
|
for (int32_t childIndex = 0; !itr->Is_Done(); ++childIndex) {
|
||||||
QString childContextName = parentContextName + QString("/") + QString(profileIterator->Get_Current_Name());
|
if (QString(itr->Get_Current_Name()) == "stepSimulation") {
|
||||||
uint64_t time = (uint64_t)((btScalar)MSECS_PER_SECOND * profileIterator->Get_Current_Total_Time());
|
itr->Enter_Child(childIndex);
|
||||||
PerformanceTimer::addTimerRecord(childContextName, time);
|
StatsWriter writer(filename);
|
||||||
profileIterator->Next();
|
writer.recurse(itr, "");
|
||||||
++numChildren;
|
break;
|
||||||
|
}
|
||||||
|
itr->Next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// recurse the children
|
|
||||||
for (int32_t i = 0; i < numChildren; ++i) {
|
|
||||||
profileIterator->Enter_Child(i);
|
|
||||||
recursivelyHarvestPerformanceStats(profileIterator, contextName);
|
|
||||||
}
|
|
||||||
// retreat back to parent
|
|
||||||
profileIterator->Enter_Parent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) {
|
void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const btCollisionObject* objectB) {
|
||||||
|
@ -399,6 +467,7 @@ void PhysicsEngine::doOwnershipInfection(const btCollisionObject* objectA, const
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsEngine::updateContactMap() {
|
void PhysicsEngine::updateContactMap() {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "updateContactMap");
|
||||||
BT_PROFILE("updateContactMap");
|
BT_PROFILE("updateContactMap");
|
||||||
++_numContactFrames;
|
++_numContactFrames;
|
||||||
|
|
||||||
|
@ -515,10 +584,21 @@ const VectorOfMotionStates& PhysicsEngine::getChangedMotionStates() {
|
||||||
void PhysicsEngine::dumpStatsIfNecessary() {
|
void PhysicsEngine::dumpStatsIfNecessary() {
|
||||||
if (_dumpNextStats) {
|
if (_dumpNextStats) {
|
||||||
_dumpNextStats = false;
|
_dumpNextStats = false;
|
||||||
|
CProfileManager::Increment_Frame_Counter();
|
||||||
|
if (_saveNextStats) {
|
||||||
|
_saveNextStats = false;
|
||||||
|
printPerformanceStatsToFile(_statsFilename);
|
||||||
|
}
|
||||||
CProfileManager::dumpAll();
|
CProfileManager::dumpAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysicsEngine::saveNextPhysicsStats(QString filename) {
|
||||||
|
_saveNextStats = true;
|
||||||
|
_dumpNextStats = true;
|
||||||
|
_statsFilename = filename;
|
||||||
|
}
|
||||||
|
|
||||||
// Bullet collision flags are as follows:
|
// Bullet collision flags are as follows:
|
||||||
// CF_STATIC_OBJECT= 1,
|
// CF_STATIC_OBJECT= 1,
|
||||||
// CF_KINEMATIC_OBJECT= 2,
|
// CF_KINEMATIC_OBJECT= 2,
|
||||||
|
|
|
@ -62,6 +62,7 @@ public:
|
||||||
|
|
||||||
void stepSimulation();
|
void stepSimulation();
|
||||||
void harvestPerformanceStats();
|
void harvestPerformanceStats();
|
||||||
|
void printPerformanceStatsToFile(const QString& filename);
|
||||||
void updateContactMap();
|
void updateContactMap();
|
||||||
|
|
||||||
bool hasOutgoingChanges() const { return _hasOutgoingChanges; }
|
bool hasOutgoingChanges() const { return _hasOutgoingChanges; }
|
||||||
|
@ -76,6 +77,9 @@ public:
|
||||||
/// \brief prints timings for last frame if stats have been requested.
|
/// \brief prints timings for last frame if stats have been requested.
|
||||||
void dumpStatsIfNecessary();
|
void dumpStatsIfNecessary();
|
||||||
|
|
||||||
|
/// \brief saves timings for last frame in filename
|
||||||
|
void saveNextPhysicsStats(QString filename);
|
||||||
|
|
||||||
/// \param offset position of simulation origin in domain-frame
|
/// \param offset position of simulation origin in domain-frame
|
||||||
void setOriginOffset(const glm::vec3& offset) { _originOffset = offset; }
|
void setOriginOffset(const glm::vec3& offset) { _originOffset = offset; }
|
||||||
|
|
||||||
|
@ -94,7 +98,6 @@ public:
|
||||||
private:
|
private:
|
||||||
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
QList<EntityDynamicPointer> removeDynamicsForBody(btRigidBody* body);
|
||||||
void addObjectToDynamicsWorld(ObjectMotionState* motionState);
|
void addObjectToDynamicsWorld(ObjectMotionState* motionState);
|
||||||
void recursivelyHarvestPerformanceStats(CProfileIterator* profileIterator, QString contextName);
|
|
||||||
|
|
||||||
/// \brief bump any objects that touch this one, then remove contact info
|
/// \brief bump any objects that touch this one, then remove contact info
|
||||||
void bumpAndPruneContacts(ObjectMotionState* motionState);
|
void bumpAndPruneContacts(ObjectMotionState* motionState);
|
||||||
|
@ -116,6 +119,7 @@ private:
|
||||||
QHash<QUuid, EntityDynamicPointer> _objectDynamics;
|
QHash<QUuid, EntityDynamicPointer> _objectDynamics;
|
||||||
QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody;
|
QHash<btRigidBody*, QSet<QUuid>> _objectDynamicsByBody;
|
||||||
std::set<btRigidBody*> _activeStaticBodies;
|
std::set<btRigidBody*> _activeStaticBodies;
|
||||||
|
QString _statsFilename;
|
||||||
|
|
||||||
glm::vec3 _originOffset;
|
glm::vec3 _originOffset;
|
||||||
|
|
||||||
|
@ -124,8 +128,9 @@ private:
|
||||||
uint32_t _numContactFrames = 0;
|
uint32_t _numContactFrames = 0;
|
||||||
uint32_t _numSubsteps;
|
uint32_t _numSubsteps;
|
||||||
|
|
||||||
bool _dumpNextStats = false;
|
bool _dumpNextStats { false };
|
||||||
bool _hasOutgoingChanges = false;
|
bool _saveNextStats { false };
|
||||||
|
bool _hasOutgoingChanges { false };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <LinearMath/btQuickprof.h>
|
#include <LinearMath/btQuickprof.h>
|
||||||
|
|
||||||
#include "ThreadSafeDynamicsWorld.h"
|
#include "ThreadSafeDynamicsWorld.h"
|
||||||
|
#include "Profile.h"
|
||||||
|
|
||||||
ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
||||||
btDispatcher* dispatcher,
|
btDispatcher* dispatcher,
|
||||||
|
@ -29,6 +30,7 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
|
||||||
|
|
||||||
int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps,
|
int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep, int maxSubSteps,
|
||||||
btScalar fixedTimeStep, SubStepCallback onSubStep) {
|
btScalar fixedTimeStep, SubStepCallback onSubStep) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "stepWithCB");
|
||||||
BT_PROFILE("stepSimulationWithSubstepCallback");
|
BT_PROFILE("stepSimulationWithSubstepCallback");
|
||||||
int subSteps = 0;
|
int subSteps = 0;
|
||||||
if (maxSubSteps) {
|
if (maxSubSteps) {
|
||||||
|
@ -68,11 +70,13 @@ int ThreadSafeDynamicsWorld::stepSimulationWithSubstepCallback(btScalar timeStep
|
||||||
saveKinematicState(fixedTimeStep*clampedSimulationSteps);
|
saveKinematicState(fixedTimeStep*clampedSimulationSteps);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "applyGravity");
|
||||||
BT_PROFILE("applyGravity");
|
BT_PROFILE("applyGravity");
|
||||||
applyGravity();
|
applyGravity();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0;i<clampedSimulationSteps;i++) {
|
for (int i=0;i<clampedSimulationSteps;i++) {
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "substep");
|
||||||
internalSingleStepSimulation(fixedTimeStep);
|
internalSingleStepSimulation(fixedTimeStep);
|
||||||
onSubStep();
|
onSubStep();
|
||||||
}
|
}
|
||||||
|
@ -118,7 +122,8 @@ void ThreadSafeDynamicsWorld::synchronizeMotionState(btRigidBody* body) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadSafeDynamicsWorld::synchronizeMotionStates() {
|
void ThreadSafeDynamicsWorld::synchronizeMotionStates() {
|
||||||
BT_PROFILE("synchronizeMotionStates");
|
PROFILE_RANGE(simulation_physics, "SyncMotionStates");
|
||||||
|
BT_PROFILE("syncMotionStates");
|
||||||
_changedMotionStates.clear();
|
_changedMotionStates.clear();
|
||||||
|
|
||||||
// NOTE: m_synchronizeAllMotionStates is 'false' by default for optimization.
|
// NOTE: m_synchronizeAllMotionStates is 'false' by default for optimization.
|
||||||
|
@ -161,6 +166,7 @@ void ThreadSafeDynamicsWorld::saveKinematicState(btScalar timeStep) {
|
||||||
///would like to iterate over m_nonStaticRigidBodies, but unfortunately old API allows
|
///would like to iterate over m_nonStaticRigidBodies, but unfortunately old API allows
|
||||||
///to switch status _after_ adding kinematic objects to the world
|
///to switch status _after_ adding kinematic objects to the world
|
||||||
///fix it for Bullet 3.x release
|
///fix it for Bullet 3.x release
|
||||||
|
DETAILED_PROFILE_RANGE(simulation_physics, "saveKinematicState");
|
||||||
BT_PROFILE("saveKinematicState");
|
BT_PROFILE("saveKinematicState");
|
||||||
for (int i=0;i<m_collisionObjects.size();i++)
|
for (int i=0;i<m_collisionObjects.size();i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QStandardPaths>
|
#include <QtCore/QStandardPaths>
|
||||||
#include <QtCore/QDateTime>
|
|
||||||
|
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QFile>
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
|
@ -31,6 +30,8 @@
|
||||||
|
|
||||||
#include "Gzip.h"
|
#include "Gzip.h"
|
||||||
#include "PortableHighResolutionClock.h"
|
#include "PortableHighResolutionClock.h"
|
||||||
|
#include "SharedLogging.h"
|
||||||
|
#include "shared/FileUtils.h"
|
||||||
#include "shared/GlobalAppProperties.h"
|
#include "shared/GlobalAppProperties.h"
|
||||||
|
|
||||||
using namespace tracing;
|
using namespace tracing;
|
||||||
|
@ -104,30 +105,13 @@ void TraceEvent::writeJson(QTextStream& out) const {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tracer::serialize(const QString& originalPath) {
|
void Tracer::serialize(const QString& filename) {
|
||||||
|
QString fullPath = FileUtils::replaceDateTimeTokens(filename);
|
||||||
QString path = originalPath;
|
fullPath = FileUtils::computeDocumentPath(fullPath);
|
||||||
|
if (!FileUtils::canCreateFile(fullPath)) {
|
||||||
// Filter for specific tokens potentially present in the path:
|
return;
|
||||||
auto now = QDateTime::currentDateTime();
|
|
||||||
|
|
||||||
path = path.replace("{DATE}", now.date().toString("yyyyMMdd"));
|
|
||||||
path = path.replace("{TIME}", now.time().toString("HHmm"));
|
|
||||||
|
|
||||||
// If the filename is relative, turn it into an absolute path relative to the document directory.
|
|
||||||
QFileInfo originalFileInfo(path);
|
|
||||||
if (originalFileInfo.isRelative()) {
|
|
||||||
QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
|
||||||
path = docsLocation + "/" + path;
|
|
||||||
QFileInfo info(path);
|
|
||||||
if (!info.absoluteDir().exists()) {
|
|
||||||
QString originalRelativePath = originalFileInfo.path();
|
|
||||||
QDir(docsLocation).mkpath(originalRelativePath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::list<TraceEvent> currentEvents;
|
std::list<TraceEvent> currentEvents;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(_eventsMutex);
|
std::lock_guard<std::mutex> guard(_eventsMutex);
|
||||||
|
@ -137,11 +121,6 @@ void Tracer::serialize(const QString& originalPath) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the file exists and we can't remove it, fail early
|
|
||||||
if (QFileInfo(path).exists() && !QFile::remove(path)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can't open a temp file for writing, fail early
|
// If we can't open a temp file for writing, fail early
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
{
|
{
|
||||||
|
@ -159,15 +138,16 @@ void Tracer::serialize(const QString& originalPath) {
|
||||||
out << "\n]";
|
out << "\n]";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.endsWith(".gz")) {
|
if (fullPath.endsWith(".gz")) {
|
||||||
QByteArray compressed;
|
QByteArray compressed;
|
||||||
gzip(data, compressed);
|
gzip(data, compressed);
|
||||||
data = compressed;
|
data = compressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
QFile file(path);
|
QFile file(fullPath);
|
||||||
if (!file.open(QIODevice::WriteOnly)) {
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
qDebug(shared) << "failed to open file '" << fullPath << "'";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file.write(data);
|
file.write(data);
|
||||||
|
@ -191,7 +171,6 @@ void Tracer::serialize(const QString& originalPath) {
|
||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
data = document.toJson(QJsonDocument::Compact);
|
data = document.toJson(QJsonDocument::Compact);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include "FileUtils.h"
|
#include "FileUtils.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QProcess>
|
#include <QtCore/QProcess>
|
||||||
|
@ -20,6 +21,8 @@
|
||||||
#include <QtCore/QRegularExpression>
|
#include <QtCore/QRegularExpression>
|
||||||
#include <QtGui/QDesktopServices>
|
#include <QtGui/QDesktopServices>
|
||||||
|
|
||||||
|
#include "../SharedLogging.h"
|
||||||
|
|
||||||
|
|
||||||
QString FileUtils::readFile(const QString& filename) {
|
QString FileUtils::readFile(const QString& filename) {
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
|
@ -82,20 +85,54 @@ QString FileUtils::standardPath(QString subfolder) {
|
||||||
// standard path
|
// standard path
|
||||||
// Mac: ~/Library/Application Support/Interface
|
// Mac: ~/Library/Application Support/Interface
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
|
||||||
|
|
||||||
if (!subfolder.startsWith("/")) {
|
if (!subfolder.startsWith("/")) {
|
||||||
subfolder.prepend("/");
|
subfolder.prepend("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!subfolder.endsWith("/")) {
|
if (!subfolder.endsWith("/")) {
|
||||||
subfolder.append("/");
|
subfolder.append("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
path.append(subfolder);
|
path.append(subfolder);
|
||||||
QDir logDir(path);
|
QDir logDir(path);
|
||||||
if (!logDir.exists(path)) {
|
if (!logDir.exists(path)) {
|
||||||
logDir.mkpath(path);
|
logDir.mkpath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FileUtils::replaceDateTimeTokens(const QString& originalPath) {
|
||||||
|
// Filter for specific tokens potentially present in the path:
|
||||||
|
auto now = QDateTime::currentDateTime();
|
||||||
|
QString path = originalPath;
|
||||||
|
path.replace("{DATE}", now.date().toString("yyyyMMdd"));
|
||||||
|
path.replace("{TIME}", now.time().toString("HHmm"));
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString FileUtils::computeDocumentPath(const QString& originalPath) {
|
||||||
|
// If the filename is relative, turn it into an absolute path relative to the document directory.
|
||||||
|
QString path = originalPath;
|
||||||
|
QFileInfo originalFileInfo(originalPath);
|
||||||
|
if (originalFileInfo.isRelative()) {
|
||||||
|
QString docsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||||
|
path = docsLocation + "/" + originalPath;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileUtils::canCreateFile(const QString& fullPath) {
|
||||||
|
// If the file exists and we can't remove it, fail early
|
||||||
|
QFileInfo fileInfo(fullPath);
|
||||||
|
if (fileInfo.exists() && !QFile::remove(fullPath)) {
|
||||||
|
qDebug(shared) << "unable to overwrite file '" << fullPath << "'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QDir dir(fileInfo.absolutePath());
|
||||||
|
if (!dir.exists()) {
|
||||||
|
if (!dir.mkpath(fullPath)) {
|
||||||
|
qDebug(shared) << "unable to create dir '" << dir.absolutePath() << "'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,9 @@ public:
|
||||||
static QString standardPath(QString subfolder);
|
static QString standardPath(QString subfolder);
|
||||||
static QString readFile(const QString& filename);
|
static QString readFile(const QString& filename);
|
||||||
static QStringList readLines(const QString& filename, QString::SplitBehavior splitBehavior = QString::KeepEmptyParts);
|
static QStringList readLines(const QString& filename, QString::SplitBehavior splitBehavior = QString::KeepEmptyParts);
|
||||||
|
static QString replaceDateTimeTokens(const QString& path);
|
||||||
|
static QString computeDocumentPath(const QString& path);
|
||||||
|
static bool canCreateFile(const QString& fullPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_FileUtils_h
|
#endif // hifi_FileUtils_h
|
||||||
|
|
|
@ -199,7 +199,7 @@
|
||||||
var purchasesElement = document.createElement('a');
|
var purchasesElement = document.createElement('a');
|
||||||
var dropDownElement = document.getElementById('user-dropdown');
|
var dropDownElement = document.getElementById('user-dropdown');
|
||||||
|
|
||||||
$('#user-dropdown').find('.username')[0].style = "max-width:80px;white-space:nowrap;overflow:hidden;" +
|
$('#user-dropdown').find('.username')[0].style = "max-width:80px;white-space:nowrap;overflow:hidden;" +
|
||||||
"text-overflow:ellipsis;display:inline-block;position:relative;top:4px;";
|
"text-overflow:ellipsis;display:inline-block;position:relative;top:4px;";
|
||||||
$('#user-dropdown').find('.caret')[0].style = "position:relative;top:-3px;";
|
$('#user-dropdown').find('.caret')[0].style = "position:relative;top:-3px;";
|
||||||
|
|
||||||
|
@ -389,27 +389,40 @@
|
||||||
|
|
||||||
var href = purchaseButton.attr('href');
|
var href = purchaseButton.attr('href');
|
||||||
purchaseButton.attr('href', '#');
|
purchaseButton.attr('href', '#');
|
||||||
purchaseButton.css({
|
var availability = $.trim($('.item-availability').text());
|
||||||
"background": "linear-gradient(#00b4ef, #0093C5)",
|
if (availability === 'available') {
|
||||||
"color": "#FFF",
|
purchaseButton.css({
|
||||||
"font-weight": "600",
|
"background": "linear-gradient(#00b4ef, #0093C5)",
|
||||||
"padding-bottom": "10px"
|
"color": "#FFF",
|
||||||
});
|
"font-weight": "600",
|
||||||
|
"padding-bottom": "10px"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
purchaseButton.css({
|
||||||
|
"background": "linear-gradient(#a2a2a2, #fefefe)",
|
||||||
|
"color": "#000",
|
||||||
|
"font-weight": "600",
|
||||||
|
"padding-bottom": "10px"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var cost = $('.item-cost').text();
|
var cost = $('.item-cost').text();
|
||||||
|
if (availability !== 'available') {
|
||||||
if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) {
|
purchaseButton.html('UNAVAILABLE (' + availability + ')');
|
||||||
|
} else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) {
|
||||||
purchaseButton.html('PURCHASE <span class="hifi-glyph hifi-glyph-hfc" style="filter:invert(1);background-size:20px;' +
|
purchaseButton.html('PURCHASE <span class="hifi-glyph hifi-glyph-hfc" style="filter:invert(1);background-size:20px;' +
|
||||||
'width:20px;height:20px;position:relative;top:5px;"></span> ' + cost);
|
'width:20px;height:20px;position:relative;top:5px;"></span> ' + cost);
|
||||||
}
|
}
|
||||||
|
|
||||||
purchaseButton.on('click', function () {
|
purchaseButton.on('click', function () {
|
||||||
buyButtonClicked(window.location.pathname.split("/")[3],
|
if ('availabile' === availability) {
|
||||||
$('#top-center').find('h1').text(),
|
buyButtonClicked(window.location.pathname.split("/")[3],
|
||||||
$('#creator').find('.value').text(),
|
$('#top-center').find('h1').text(),
|
||||||
cost,
|
$('#creator').find('.value').text(),
|
||||||
href);
|
cost,
|
||||||
});
|
href);
|
||||||
|
}
|
||||||
|
});
|
||||||
maybeAddPurchasesButton();
|
maybeAddPurchasesButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|