diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 9d50f60c01..5e5bbba0bf 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -184,6 +184,7 @@ void Agent::run() { loop.exec(); QString scriptContents(reply->readAll()); + delete reply; qDebug() << "Downloaded script:" << scriptContents; diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 0311f8562b..6b646c1dad 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -60,7 +60,7 @@ #include "AudioMixer.h" const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f; -const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18; +const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18f; const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.003f; const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; const QString AUDIO_ENV_GROUP_KEY = "audio_env"; diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 3da06a9c0e..a094432af2 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1736,6 +1736,9 @@ bool DomainServer::handleHTTPSRequest(HTTPSConnection* connection, const QUrl &u connection->respond(HTTPConnection::StatusCode302, QByteArray(), HTTPConnection::DefaultContentType, cookieHeaders); + delete tokenReply; + delete profileReply; + // we've redirected the user back to our homepage return true; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1364e1b05b..22a2b6bc22 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3770,9 +3770,9 @@ void Application::parseVersionXml() { QString latestVersion; QUrl downloadUrl; QString releaseNotes("Unavailable"); - QObject* sender = QObject::sender(); + QNetworkReply* sender = qobject_cast(QObject::sender()); - QXmlStreamReader xml(qobject_cast(sender)); + QXmlStreamReader xml(sender); while (!xml.atEnd() && !xml.hasError()) { if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) { diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ddbaeece72..5b10cbd1a5 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -454,47 +454,6 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode, bool return; } renderDisplayName(); - - if (!_chatMessage.empty()) { - int width = 0; - int lastWidth = 0; - for (string::iterator it = _chatMessage.begin(); it != _chatMessage.end(); it++) { - width += (lastWidth = textRenderer(CHAT)->computeWidth(*it)); - } - glPushMatrix(); - - glm::vec3 chatPosition = getHead()->getEyePosition() + getBodyUpDirection() * CHAT_MESSAGE_HEIGHT * _scale; - glTranslatef(chatPosition.x, chatPosition.y, chatPosition.z); - glm::quat chatRotation = Application::getInstance()->getCamera()->getRotation(); - glm::vec3 chatAxis = glm::axis(chatRotation); - glRotatef(glm::degrees(glm::angle(chatRotation)), chatAxis.x, chatAxis.y, chatAxis.z); - - glColor3f(0.0f, 0.8f, 0.0f); - glRotatef(180.0f, 0.0f, 1.0f, 0.0f); - glRotatef(180.0f, 0.0f, 0.0f, 1.0f); - glScalef(_scale * CHAT_MESSAGE_SCALE, _scale * CHAT_MESSAGE_SCALE, 1.0f); - - glDisable(GL_LIGHTING); - glDepthMask(false); - if (_keyState == NO_KEY_DOWN) { - textRenderer(CHAT)->draw(-width / 2.0f, 0, _chatMessage.c_str()); - - } else { - // rather than using substr and allocating a new string, just replace the last - // character with a null, then restore it - int lastIndex = _chatMessage.size() - 1; - char lastChar = _chatMessage[lastIndex]; - _chatMessage[lastIndex] = '\0'; - textRenderer(CHAT)->draw(-width / 2.0f, 0, _chatMessage.c_str()); - _chatMessage[lastIndex] = lastChar; - glColor3f(0.0f, 1.0f, 0.0f); - textRenderer(CHAT)->draw(width / 2.0f - lastWidth, 0, _chatMessage.c_str() + lastIndex); - } - glEnable(GL_LIGHTING); - glDepthMask(true); - - glPopMatrix(); - } } glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 2e6c166bd3..3523cb2176 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -49,16 +49,22 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX Avatar* owningAvatar = static_cast(_owningHead->_owningAvatar); // get the rotation axes in joint space and use them to adjust the rotation glm::mat3 axes = glm::mat3_cast(glm::quat()); - glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInConstrainedFrame()) * - joint.preTransform * glm::mat4_cast(joint.preRotation))); - state.setRotationInConstrainedFrame( - glm::angleAxis(- RADIANS_PER_DEGREE * (_owningHead->getFinalRoll() - owningAvatar->getHead()->getFinalLeanSideways()), - glm::normalize(inverse * axes[2])) - * glm::angleAxis(RADIANS_PER_DEGREE * (_owningHead->getFinalYaw() - _owningHead->getTorsoTwist()), - glm::normalize(inverse * axes[1])) - * glm::angleAxis(- RADIANS_PER_DEGREE * (_owningHead->getFinalPitch() - owningAvatar->getHead()->getFinalLeanForward()), - glm::normalize(inverse * axes[0])) - * joint.rotation, DEFAULT_PRIORITY); + glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * + glm::translate(state.getDefaultTranslationInConstrainedFrame()) * + joint.preTransform * glm::mat4_cast(joint.preRotation))); + glm::vec3 pitchYawRoll = safeEulerAngles(_owningHead->getFinalOrientationInLocalFrame()); + if (owningAvatar->isMyAvatar()) { + glm::vec3 lean = glm::radians(glm::vec3(_owningHead->getFinalLeanForward(), + _owningHead->getTorsoTwist(), + _owningHead->getFinalLeanSideways())); + pitchYawRoll -= lean; + } + + state.setRotationInConstrainedFrame(glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * axes[2])) + * glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * axes[1])) + * glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * axes[0])) + * joint.rotation, DEFAULT_PRIORITY); + } void FaceModel::maybeUpdateEyeRotation(Model* model, const JointState& parentState, const FBXJoint& joint, JointState& state) { diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp index f4a509baa6..660b9009b6 100644 --- a/interface/src/ui/ScriptEditorWidget.cpp +++ b/interface/src/ui/ScriptEditorWidget.cpp @@ -158,6 +158,8 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) { QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); _scriptEditorWidgetUI->scriptEdit->setPlainText(reply->readAll()); + delete reply; + if (!saveAs()) { static_cast(this->parent()->parent()->parent())->terminateCurrentTab(); } diff --git a/interface/src/ui/SnapshotShareDialog.cpp b/interface/src/ui/SnapshotShareDialog.cpp index 046aa4d12c..47914e0a12 100644 --- a/interface/src/ui/SnapshotShareDialog.cpp +++ b/interface/src/ui/SnapshotShareDialog.cpp @@ -148,6 +148,7 @@ void SnapshotShareDialog::postRequestFinished() { QNetworkReply* requestReply = reinterpret_cast(sender()); QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll()); + requestReply->deleteLater(); const QJsonObject& responseObject = jsonResponse.object(); if (responseObject.contains("id")) { diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index bcb69bdce3..b34416f566 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -205,6 +205,7 @@ void BillboardOverlay::replyFinished() { QNetworkReply* reply = static_cast(sender()); _billboard = reply->readAll(); _isLoaded = true; + reply->deleteLater(); } bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index d7a6889c05..1ecfc74e44 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -66,6 +66,7 @@ void ImageOverlay::replyFinished() { _textureImage.loadFromData(rawData); _renderImage = true; _isLoaded = true; + reply->deleteLater(); } void ImageOverlay::render(RenderArgs* args) { diff --git a/libraries/audio/src/AudioConstants.h b/libraries/audio/src/AudioConstants.h index 91d6db38f7..6c4e582117 100644 --- a/libraries/audio/src/AudioConstants.h +++ b/libraries/audio/src/AudioConstants.h @@ -26,8 +26,8 @@ namespace AudioConstants { const int NETWORK_FRAME_BYTES_PER_CHANNEL = 512; const int NETWORK_FRAME_SAMPLES_PER_CHANNEL = NETWORK_FRAME_BYTES_PER_CHANNEL / sizeof(AudioSample); const float NETWORK_FRAME_MSECS = (AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL - / (float) AudioConstants::SAMPLE_RATE) * 1000.0; - const unsigned int NETWORK_FRAME_USECS = floorf(NETWORK_FRAME_MSECS * 1000.0); + / (float)AudioConstants::SAMPLE_RATE) * 1000.0f; + const unsigned int NETWORK_FRAME_USECS = (unsigned int)floorf(NETWORK_FRAME_MSECS * 1000.0f); const int MIN_SAMPLE_VALUE = std::numeric_limits::min(); const int MAX_SAMPLE_VALUE = std::numeric_limits::max(); } diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 2608c333d6..54ff61d66a 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -80,6 +80,7 @@ void Sound::downloadFinished(QNetworkReply* reply) { } _isReady = true; + reply->deleteLater(); } void Sound::downSample(const QByteArray& rawAudioByteArray) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d5b6c29362..cc45da610f 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -175,11 +175,6 @@ QByteArray AvatarData::toByteArray() { // Instantaneous audio loudness (used to drive facial animation) memcpy(destinationBuffer, &_headData->_audioLoudness, sizeof(float)); destinationBuffer += sizeof(float); - - // chat message - *destinationBuffer++ = _chatMessage.size(); - memcpy(destinationBuffer, _chatMessage.data(), _chatMessage.size() * sizeof(char)); - destinationBuffer += _chatMessage.size() * sizeof(char); // bitMask of less than byte wide items unsigned char bitItems = 0; @@ -300,11 +295,10 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) { // lookAt = 12 // audioLoudness = 4 // } - // + 1 byte for messageSize (0) // + 1 byte for pupilSize // + 1 byte for numJoints (0) // = 53 bytes - int minPossibleSize = 53; + int minPossibleSize = 52; int maxAvailableSize = packet.size() - offset; if (minPossibleSize > maxAvailableSize) { @@ -420,23 +414,6 @@ int AvatarData::parseDataAtOffset(const QByteArray& packet, int offset) { _headData->_audioLoudness = audioLoudness; } // 4 bytes - // chat - int chatMessageSize = *sourceBuffer++; - minPossibleSize += chatMessageSize; - if (minPossibleSize > maxAvailableSize) { - if (shouldLogError(now)) { - qDebug() << "Malformed AvatarData packet before ChatMessage;" - << " displayName = '" << _displayName << "'" - << " minPossibleSize = " << minPossibleSize - << " maxAvailableSize = " << maxAvailableSize; - } - return maxAvailableSize; - } - { // chat payload - _chatMessage = string((char*)sourceBuffer, chatMessageSize); - sourceBuffer += chatMessageSize * sizeof(char); - } // 1 + chatMessageSize bytes - { // bitFlags and face data unsigned char bitItems = *sourceBuffer++; diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 6564d53317..84653ef673 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -32,17 +32,17 @@ typedef unsigned long long quint64; #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include #include +#include +#include #include +#include +#include #include #include @@ -130,7 +130,6 @@ class AvatarData : public QObject { Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw) Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch) Q_PROPERTY(float bodyRoll READ getBodyRoll WRITE setBodyRoll) - Q_PROPERTY(QString chatMessage READ getQStringChatMessage WRITE setChatMessage) Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(glm::quat headOrientation READ getHeadOrientation WRITE setHeadOrientation) @@ -242,12 +241,6 @@ public: void setKeyState(KeyState s) { _keyState = s; } KeyState keyState() const { return _keyState; } - // chat message - void setChatMessage(const std::string& msg) { _chatMessage = msg; } - void setChatMessage(const QString& string) { _chatMessage = string.toLocal8Bit().constData(); } - const std::string& setChatMessage() const { return _chatMessage; } - QString getQStringChatMessage() { return QString(_chatMessage.data()); } - bool isChatCirclingEnabled() const { return _isChatCirclingEnabled; } const HeadData* getHeadData() const { return _headData; } const HandData* getHandData() const { return _handData; } @@ -355,9 +348,6 @@ protected: // key state KeyState _keyState; - // chat message - std::string _chatMessage; - bool _isChatCirclingEnabled; bool _forceFaceshiftConnected; bool _hasNewJointRotations; // set in AvatarData, cleared in Avatar @@ -396,7 +386,6 @@ private: AvatarData(const AvatarData&); AvatarData& operator= (const AvatarData&); }; - Q_DECLARE_METATYPE(AvatarData*) class JointData { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 4f0f1c80a8..47e9237ddc 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -145,6 +145,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe } else { qDebug() << "ERROR Loading file:" << url.toString(); } + delete reply; } } diff --git a/libraries/entities/src/DeleteEntityOperator.cpp b/libraries/entities/src/DeleteEntityOperator.cpp index 12441e5427..890c301682 100644 --- a/libraries/entities/src/DeleteEntityOperator.cpp +++ b/libraries/entities/src/DeleteEntityOperator.cpp @@ -91,13 +91,9 @@ bool DeleteEntityOperator::preRecursion(OctreeElement* element) { // If this is the element we're looking for, then ask it to remove the old entity // and we can stop searching. if (entityTreeElement == details.containingElement) { - EntityItemID entityItemID = details.entity->getEntityItemID(); - EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID); // find the actual entity - assert(theEntity); - _tree->trackDeletedEntity(theEntity); - entityTreeElement->removeEntityItem(theEntity); // remove it from the element - _tree->setContainingElement(entityItemID, NULL); // update or id to element lookup - delete theEntity; // now actually delete the entity! + EntityItem* theEntity = details.entity; + assert(entityTreeElement->removeEntityItem(theEntity)); // remove it from the element + _tree->setContainingElement(details.entity->getEntityItemID(), NULL); // update or id to element lookup _foundCount++; } } diff --git a/libraries/entities/src/DeleteEntityOperator.h b/libraries/entities/src/DeleteEntityOperator.h index a3119da0b9..b6e6f9e2ff 100644 --- a/libraries/entities/src/DeleteEntityOperator.h +++ b/libraries/entities/src/DeleteEntityOperator.h @@ -14,11 +14,13 @@ class EntityToDeleteDetails { public: - const EntityItem* entity; + EntityItem* entity; AACube cube; EntityTreeElement* containingElement; }; +typedef QSet RemovedEntities; + inline uint qHash(const EntityToDeleteDetails& a, uint seed) { return qHash(a.entity->getEntityItemID(), seed); } @@ -36,9 +38,11 @@ public: void addEntityIDToDeleteList(const EntityItemID& searchEntityID); virtual bool preRecursion(OctreeElement* element); virtual bool postRecursion(OctreeElement* element); + + const RemovedEntities& getEntities() const { return _entitiesToDelete; } private: EntityTree* _tree; - QSet _entitiesToDelete; + RemovedEntities _entitiesToDelete; quint64 _changeTime; int _foundCount; int _lookingCount; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 3a2b8c64b1..a744d39f05 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1019,7 +1019,6 @@ void EntityItem::recalculateCollisionShape() { } const float MIN_POSITION_DELTA = 0.0001f; -const float MIN_DIMENSION_DELTA = 0.0001f; const float MIN_ALIGNMENT_DOT = 0.9999f; const float MIN_MASS_DELTA = 0.001f; const float MIN_VELOCITY_DELTA = 0.025f; @@ -1045,17 +1044,17 @@ void EntityItem::updatePositionInMeters(const glm::vec3& value) { } void EntityItem::updateDimensions(const glm::vec3& value) { - if (glm::distance(_dimensions, value) * (float)TREE_SCALE > MIN_DIMENSION_DELTA) { - _dimensions = value; + if (_dimensions != value) { + _dimensions = glm::abs(value); recalculateCollisionShape(); _dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS); } } void EntityItem::updateDimensionsInMeters(const glm::vec3& value) { - glm::vec3 dimensions = value / (float) TREE_SCALE; - if (glm::distance(_dimensions, dimensions) * (float)TREE_SCALE > MIN_DIMENSION_DELTA) { - _dimensions = dimensions; + glm::vec3 dimensions = glm::abs(value) / (float) TREE_SCALE; + if (_dimensions != dimensions) { + _dimensions = dimensions; recalculateCollisionShape(); _dirtyFlags |= (EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS); } diff --git a/libraries/entities/src/EntitySimulation.cpp b/libraries/entities/src/EntitySimulation.cpp index 09f75c9ebf..f7d6c55803 100644 --- a/libraries/entities/src/EntitySimulation.cpp +++ b/libraries/entities/src/EntitySimulation.cpp @@ -38,6 +38,7 @@ void EntitySimulation::updateEntities(QSet& entitiesToDelete) { _entitiesToDelete.clear(); } +// private void EntitySimulation::expireMortalEntities(const quint64& now) { if (now > _nextExpiry) { // only search for expired entities if we expect to find one @@ -63,6 +64,7 @@ void EntitySimulation::expireMortalEntities(const quint64& now) { } } +// private void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) { PerformanceTimer perfTimer("updatingEntities"); QSet::iterator itemItr = _updateableEntities.begin(); @@ -79,6 +81,7 @@ void EntitySimulation::callUpdateOnEntitiesThatNeedIt(const quint64& now) { } } +// private void EntitySimulation::sortEntitiesThatMoved() { // 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. diff --git a/libraries/entities/src/EntitySimulation.h b/libraries/entities/src/EntitySimulation.h index a32a1226c7..dee6b1c43a 100644 --- a/libraries/entities/src/EntitySimulation.h +++ b/libraries/entities/src/EntitySimulation.h @@ -33,9 +33,12 @@ const int DIRTY_SIMULATION_FLAGS = class EntitySimulation { public: - EntitySimulation() : _entityTree(NULL) { } + EntitySimulation() : _mutex(QMutex::Recursive), _entityTree(NULL) { } virtual ~EntitySimulation() { setEntityTree(NULL); } + void lock() { _mutex.lock(); } + void unlock() { _mutex.unlock(); } + /// \param tree pointer to EntityTree which is stored internally void setEntityTree(EntityTree* tree); @@ -80,6 +83,8 @@ protected: void callUpdateOnEntitiesThatNeedIt(const quint64& now); void sortEntitiesThatMoved(); + QMutex _mutex; + // back pointer to EntityTree structure EntityTree* _entityTree; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 73d944b084..580fed8790 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -15,7 +15,6 @@ #include "EntitySimulation.h" #include "AddEntityOperator.h" -#include "DeleteEntityOperator.h" #include "MovingEntitiesOperator.h" #include "UpdateEntityOperator.h" @@ -40,7 +39,9 @@ EntityTreeElement* EntityTree::createNewElement(unsigned char * octalCode) { void EntityTree::eraseAllOctreeElements(bool createNewRoot) { // this would be a good place to clean up our entities... if (_simulation) { + _simulation->lock(); _simulation->clearEntities(); + _simulation->unlock(); } foreach (EntityTreeElement* element, _entityToElementMap) { element->cleanupEntities(); @@ -84,7 +85,9 @@ void EntityTree::postAddEntity(EntityItem* entity) { assert(entity); // check to see if we need to simulate this entity.. if (_simulation) { + _simulation->lock(); _simulation->addEntity(entity); + _simulation->unlock(); } _isDirty = true; emit addingEntity(entity->getEntityItemID()); @@ -141,7 +144,9 @@ bool EntityTree::updateEntityWithElement(EntityItem* entity, const EntityItemPro if (newFlags) { if (_simulation) { if (newFlags & DIRTY_SIMULATION_FLAGS) { + _simulation->lock(); _simulation->entityChanged(entity); + _simulation->unlock(); } } else { // normally the _simulation clears ALL updateFlags, but since there is none we do it explicitly @@ -204,21 +209,6 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem return result; } - -void EntityTree::trackDeletedEntity(EntityItem* entity) { - if (_simulation) { - _simulation->removeEntity(entity); - } - // this is only needed on the server to send delete messages for recently deleted entities to the viewers - if (getIsServer()) { - // set up the deleted entities ID - quint64 deletedAt = usecTimestampNow(); - _recentlyDeletedEntitiesLock.lockForWrite(); - _recentlyDeletedEntityItemIDs.insert(deletedAt, entity->getEntityItemID().id); - _recentlyDeletedEntitiesLock.unlock(); - } -} - void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) { emit entityScriptChanging(entityItemID); } @@ -231,7 +221,9 @@ void EntityTree::setSimulation(EntitySimulation* simulation) { if (_simulation && _simulation != simulation) { // It's important to clearEntities() on the simulation since taht will update each // EntityItem::_simulationState correctly so as to not confuse the next _simulation. + _simulation->lock(); _simulation->clearEntities(); + _simulation->unlock(); } _simulation = simulation; } @@ -242,6 +234,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID) { // NOTE: callers must lock the tree before using this method DeleteEntityOperator theOperator(this, entityID); recurseTreeWithOperator(&theOperator); + processRemovedEntities(theOperator); _isDirty = true; } @@ -255,9 +248,36 @@ void EntityTree::deleteEntities(QSet entityIDs) { } recurseTreeWithOperator(&theOperator); + processRemovedEntities(theOperator); _isDirty = true; } +void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) { + const RemovedEntities& entities = theOperator.getEntities(); + if (_simulation) { + _simulation->lock(); + } + foreach(const EntityToDeleteDetails& details, entities) { + EntityItem* theEntity = details.entity; + + if (getIsServer()) { + // set up the deleted entities ID + quint64 deletedAt = usecTimestampNow(); + _recentlyDeletedEntitiesLock.lockForWrite(); + _recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID().id); + _recentlyDeletedEntitiesLock.unlock(); + } + + if (_simulation) { + _simulation->removeEntity(theEntity); + } + delete theEntity; // now actually delete the entity! + } + if (_simulation) { + _simulation->unlock(); + } +} + /// This method is used to find and fix entity IDs that are shifting from creator token based to known ID based entity IDs. /// This should only be used on a client side (viewing) tree. The typical usage is that a local editor has been creating /// entities in the local tree, those entities have creatorToken based entity IDs. But those entity edits are also sent up to @@ -592,7 +612,9 @@ void EntityTree::releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncod void EntityTree::entityChanged(EntityItem* entity) { if (_simulation) { + _simulation->lock(); _simulation->entityChanged(entity); + _simulation->unlock(); } } @@ -600,7 +622,9 @@ void EntityTree::update() { if (_simulation) { lockForWrite(); QSet entitiesToDelete; + _simulation->lock(); _simulation->updateEntities(entitiesToDelete); + _simulation->unlock(); if (entitiesToDelete.size() > 0) { // translate into list of ID's QSet idsToDelete; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index cfd08c3b5c..e8c8a93165 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -16,6 +16,7 @@ #include #include "EntityTreeElement.h" +#include "DeleteEntityOperator.h" class Model; @@ -146,8 +147,6 @@ public: void entityChanged(EntityItem* entity); - void trackDeletedEntity(EntityItem* entity); - void emitEntityScriptChanging(const EntityItemID& entityItemID); void setSimulation(EntitySimulation* simulation); @@ -160,6 +159,7 @@ signals: private: + void processRemovedEntities(const DeleteEntityOperator& theOperator); bool updateEntityWithElement(EntityItem* entity, const EntityItemProperties& properties, EntityTreeElement* containingElement); static bool findNearPointOperation(OctreeElement* element, void* extraData); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 126ef27414..e4f31e666e 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -240,6 +240,7 @@ void DomainHandler::settingsRequestFinished() { requestDomainSettings(); } } + settingsReply->deleteLater(); } void DomainHandler::parseDTLSRequirementPacket(const QByteArray& dtlsRequirementPacket) { diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index d5c53f8fcf..b1790ac4ce 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -57,7 +57,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeInjectAudio: return 1; case PacketTypeAvatarData: - return 3; + return 4; case PacketTypeAvatarIdentity: return 1; case PacketTypeEnvironmentData: diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 5bb327fe63..4b769c9a28 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -67,13 +67,27 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& _resources.insert(url, resource); } else { - _unusedResources.remove(resource->getLRUKey()); + removeUnusedResource(resource); } return resource; } void ResourceCache::addUnusedResource(const QSharedPointer& resource) { + static const int BYTES_PER_MEGABYTES = 1024 * 1024; const int RETAINED_RESOURCE_COUNT = 50; + const int RETAINED_RESOURCE_SIZE = 100 * BYTES_PER_MEGABYTES; + + while (_unusedResourcesTotalBytes + resource->getBytesTotal() > RETAINED_RESOURCE_SIZE && + !_unusedResources.empty()) { + // unload the oldest resource + QMap >::iterator it = _unusedResources.begin(); + + _unusedResourcesTotalBytes -= it.value()->getBytesTotal(); + it.value()->setCache(NULL); + _unusedResources.erase(it); + } + + if (_unusedResources.size() > RETAINED_RESOURCE_COUNT) { // unload the oldest resource QMap >::iterator it = _unusedResources.begin(); @@ -82,6 +96,14 @@ void ResourceCache::addUnusedResource(const QSharedPointer& resource) } resource->setLRUKey(++_lastLRUKey); _unusedResources.insert(resource->getLRUKey(), resource); + _unusedResourcesTotalBytes += resource->getBytesTotal(); +} + +void ResourceCache::removeUnusedResource(const QSharedPointer& resource) { + if (_unusedResources.contains(resource->getLRUKey())) { + _unusedResources.remove(resource->getLRUKey()); + _unusedResourcesTotalBytes -= resource->getBytesTotal(); + } } void ResourceCache::attemptRequest(Resource* resource) { diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index e1b6327652..0a4121ca5e 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -45,7 +45,8 @@ public: void refresh(const QUrl& url); protected: - + + qint64 _unusedResourcesTotalBytes = 0; QMap > _unusedResources; /// Loads a resource from the specified URL. @@ -58,8 +59,9 @@ protected: /// Creates a new resource. virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) = 0; - + void addUnusedResource(const QSharedPointer& resource); + void removeUnusedResource(const QSharedPointer& resource); static void attemptRequest(Resource* resource); static void requestCompleted(Resource* resource); diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index edff3f8291..4129f1c7b0 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -169,7 +169,7 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) { _collisionDispatcher = new btCollisionDispatcher(_collisionConfig); _broadphaseFilter = new btDbvtBroadphase(); _constraintSolver = new btSequentialImpulseConstraintSolver; - _dynamicsWorld = new ThreadSafeDynamicsWorld(_collisionDispatcher, _broadphaseFilter, _constraintSolver, _collisionConfig, _entityTree); + _dynamicsWorld = new ThreadSafeDynamicsWorld(_collisionDispatcher, _broadphaseFilter, _constraintSolver, _collisionConfig); // default gravity of the world is zero, so each object must specify its own gravity // TODO: set up gravity zones @@ -200,13 +200,14 @@ void PhysicsEngine::init(EntityEditPacketSender* packetSender) { const float FIXED_SUBSTEP = 1.0f / 60.0f; void PhysicsEngine::stepSimulation() { + lock(); // NOTE: the grand order of operations is: // (1) relay incoming changes // (2) step simulation // (3) synchronize outgoing motion states // (4) send outgoing packets - // this is step (1) + // This is step (1). relayIncomingChangesToSimulation(); const int MAX_NUM_SUBSTEPS = 4; @@ -215,9 +216,24 @@ void PhysicsEngine::stepSimulation() { _clock.reset(); float timeStep = btMin(dt, MAX_TIMESTEP); - // steps (2) and (3) are performed by ThreadSafeDynamicsWorld::stepSimulation()) + // This is step (2). int numSubSteps = _dynamicsWorld->stepSimulation(timeStep, MAX_NUM_SUBSTEPS, FIXED_SUBSTEP); _frameCount += (uint32_t)numSubSteps; + unlock(); + + // This is step (3) which is done outside of stepSimulation() so we can lock _entityTree. + // + // Unfortunately we have to unlock the simulation (above) before we try to lock the _entityTree + // to avoid deadlock -- the _entityTree may try to lock its EntitySimulation (from which this + // PhysicsEngine derives) when updating/adding/deleting entities so we need to wait for our own + // lock on the tree before we re-lock ourselves. + // + // TODO: untangle these lock sequences. + _entityTree->lockForWrite(); + lock(); + _dynamicsWorld->synchronizeMotionStates(); + unlock(); + _entityTree->unlock(); } // Bullet collision flags are as follows: diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp index e70bdc7210..e204ae9e7f 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.cpp +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.cpp @@ -15,8 +15,6 @@ * Copied and modified from btDiscreteDynamicsWorld.cpp by AndrewMeadows on 2014.11.12. * */ -#include - #include "ThreadSafeDynamicsWorld.h" #ifdef USE_BULLET_PHYSICS @@ -24,17 +22,8 @@ ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld( btDispatcher* dispatcher, btBroadphaseInterface* pairCache, btConstraintSolver* constraintSolver, - btCollisionConfiguration* collisionConfiguration, - EntityTree* entities) + btCollisionConfiguration* collisionConfiguration) : btDiscreteDynamicsWorld(dispatcher, pairCache, constraintSolver, collisionConfiguration) { - assert(entities); - _entities = entities; -} - -void ThreadSafeDynamicsWorld::synchronizeMotionStates() { - _entities->lockForWrite(); - btDiscreteDynamicsWorld::synchronizeMotionStates(); - _entities->unlock(); } int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, btScalar fixedTimeStep) { @@ -82,10 +71,15 @@ int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps, } } - // We only sync motion states once at the end of all substeps. - // This is to avoid placing multiple, repeated thread locks on _entities. - synchronizeMotionStates(); + // NOTE: We do NOT call synchronizeMotionState() after each substep (to avoid multiple locks on the + // object data outside of the physics engine). A consequence of this is that the transforms of the + // external objects only ever update at the end of the full step. + + // NOTE: We do NOT call synchronizeMotionStates() here. Instead it is called by an external class + // that knows how to lock threads correctly. + clearForces(); - return subSteps; + + return subSteps; } #endif // USE_BULLET_PHYSICS diff --git a/libraries/physics/src/ThreadSafeDynamicsWorld.h b/libraries/physics/src/ThreadSafeDynamicsWorld.h index 3f3bc2517b..64d5413c56 100644 --- a/libraries/physics/src/ThreadSafeDynamicsWorld.h +++ b/libraries/physics/src/ThreadSafeDynamicsWorld.h @@ -21,8 +21,6 @@ #ifdef USE_BULLET_PHYSICS #include -class EntityTree; - ATTRIBUTE_ALIGNED16(class) ThreadSafeDynamicsWorld : public btDiscreteDynamicsWorld { public: BT_DECLARE_ALIGNED_ALLOCATOR(); @@ -31,20 +29,15 @@ public: btDispatcher* dispatcher, btBroadphaseInterface* pairCache, btConstraintSolver* constraintSolver, - btCollisionConfiguration* collisionConfiguration, - EntityTree* entities); + btCollisionConfiguration* collisionConfiguration); // virtual overrides from btDiscreteDynamicsWorld int stepSimulation( btScalar timeStep, int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.)); - void synchronizeMotionStates(); // btDiscreteDynamicsWorld::m_localTime is the portion of real-time that has not yet been simulated // but is used for MotionState::setWorldTransform() extrapolation (a feature that Bullet uses to provide // smoother rendering of objects when the physics simulation loop is ansynchronous to the render loop). float getLocalTimeAccumulation() const { return m_localTime; } - -private: - EntityTree* _entities; }; #else // USE_BULLET_PHYSICS diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 5f03e771ea..9450d99cf9 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -194,6 +194,8 @@ void ScriptEngine::handleScriptDownload() { qDebug() << "ERROR Loading file:" << reply->url().toString(); emit errorLoadingScript(_fileNameString); } + + reply->deleteLater(); } void ScriptEngine::init() { @@ -605,6 +607,7 @@ void ScriptEngine::include(const QString& includeFile) { QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); loop.exec(); includeContents = reply->readAll(); + reply->deleteLater(); } else { #ifdef _WIN32 QString fileName = url.toString(); diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index dec20dface..a756b3fe8f 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -332,7 +332,7 @@ void XMLHttpRequestClass::abortRequest() { if (_reply) { disconnectFromReply(_reply); _reply->abort(); - delete _reply; + _reply->deleteLater(); _reply = NULL; } diff --git a/tests/octree/src/OctreeTests.cpp b/tests/octree/src/OctreeTests.cpp index 05d6cb593d..4fc543d5d3 100644 --- a/tests/octree/src/OctreeTests.cpp +++ b/tests/octree/src/OctreeTests.cpp @@ -802,7 +802,7 @@ void OctreeTests::propertyFlagsTests(bool verbose) { qDebug() << "fill encoded byte array with extra garbage (as if it was bitstream with more content)"; } QByteArray extraContent; - extraContent.fill(0xba, 10); + extraContent.fill(0xbaU, 10); encoded.append(extraContent); if (verbose) { diff --git a/tools/scribe/src/TextTemplate.cpp b/tools/scribe/src/TextTemplate.cpp index 5d10e55e3f..752d4a37d8 100755 --- a/tools/scribe/src/TextTemplate.cpp +++ b/tools/scribe/src/TextTemplate.cpp @@ -837,7 +837,7 @@ int TextTemplate::evalBlockGeneration(std::ostream& dst, const BlockPointer& blo if (block->command.arguments.size()) { // THe actual value of the var defined sneeds to be evaluated: String val; - for (int t = 1; t < block->command.arguments.size(); t++) { + for (unsigned int t = 1; t < block->command.arguments.size(); t++) { // detect if a param is a var int len = block->command.arguments[t].length(); if ((block->command.arguments[t][0] == Tag::VAR)