diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 7a7210a0e8..e6b7d9d121 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -115,11 +115,7 @@ public: uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const; void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time); - QVector& getLastOtherAvatarSentJoints(QUuid otherAvatar) { - auto& lastOtherAvatarSentJoints = _lastOtherAvatarSentJoints[otherAvatar]; - lastOtherAvatarSentJoints.resize(_avatar->getJointCount()); - return lastOtherAvatarSentJoints; - } + QVector& getLastOtherAvatarSentJoints(QUuid otherAvatar) { return _lastOtherAvatarSentJoints[otherAvatar]; } void queuePacket(QSharedPointer message, SharedNodePointer node); int processPackets(); // returns number of packets processed diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index fb4b65726a..6f19b73cc5 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -381,6 +381,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) bool includeThisAvatar = true; auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID()); QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID()); + + lastSentJointsForOther.resize(otherAvatar->getJointCount()); + bool distanceAdjust = true; glm::vec3 viewerPosition = myPosition; AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 7d0b538f6e..9f24036e92 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -451,11 +451,12 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect return SharedNodePointer(); } - QUuid hintNodeID; + QUuid existingNodeID; // in case this is a node that's failing to connect // double check we don't have the same node whose sockets match exactly already in the list limitedNodeList->eachNodeBreakable([&](const SharedNodePointer& node){ + if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) { // we have a node that already has these exact sockets - this can occur if a node // is failing to connect to the domain @@ -465,15 +466,20 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect auto existingNodeData = static_cast(node->getLinkedData()); if (existingNodeData->getUsername() == username) { - hintNodeID = node->getUUID(); + qDebug() << "Deleting existing connection from same sockaddr: " << node->getUUID(); + existingNodeID = node->getUUID(); return false; } } return true; }); + if (!existingNodeID.isNull()) { + limitedNodeList->killNodeWithUUID(existingNodeID); + } + // add the connecting node (or re-use the matched one from eachNodeBreakable above) - SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection, hintNodeID); + SharedNodePointer newNode = addVerifiedNodeFromConnectRequest(nodeConnection); // set the edit rights for this user newNode->setPermissions(userPerms); diff --git a/interface/src/Crashpad.cpp b/interface/src/Crashpad.cpp index 8ed3fc23bd..ae2f341337 100644 --- a/interface/src/Crashpad.cpp +++ b/interface/src/Crashpad.cpp @@ -31,10 +31,27 @@ using namespace crashpad; static const std::string BACKTRACE_URL { CMAKE_BACKTRACE_URL }; static const std::string BACKTRACE_TOKEN { CMAKE_BACKTRACE_TOKEN }; +static std::wstring gIPCPipe; + extern QString qAppFileName(); // crashpad::AnnotationList* crashpadAnnotations { nullptr }; +#include + +LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { + if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_HEAP_CORRUPTION || + pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_BUFFER_OVERRUN) { + CrashpadClient client; + if (gIPCPipe.length()) { + client.SetHandlerIPCPipe(gIPCPipe); + } + client.DumpAndCrash(pExceptionInfo); + } + + return EXCEPTION_CONTINUE_SEARCH; +} + bool startCrashHandler() { if (BACKTRACE_URL.empty() || BACKTRACE_TOKEN.empty()) { return false; @@ -76,7 +93,12 @@ bool startCrashHandler() { // Enable automated uploads. database->GetSettings()->SetUploadsEnabled(true); - return client.StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true); + bool result = client.StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true); + gIPCPipe = client.GetHandlerIPCPipe(); + + AddVectoredExceptionHandler(0, vectoredExceptionHandler); + + return result; } void setCrashAnnotation(std::string name, std::string value) { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e74339a019..389913f7e1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -749,32 +749,32 @@ Menu::Menu() { action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashPureVirtualFunction); connect(action, &QAction::triggered, qApp, []() { crash::pureVirtualCall(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashPureVirtualFunctionThreaded); - connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::pureVirtualCall(); }); }); + connect(action, &QAction::triggered, qApp, []() { std::thread(crash::pureVirtualCall).join(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashDoubleFree); connect(action, &QAction::triggered, qApp, []() { crash::doubleFree(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashDoubleFreeThreaded); - connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::doubleFree(); }); }); + connect(action, &QAction::triggered, qApp, []() { std::thread(crash::doubleFree).join(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashAbort); connect(action, &QAction::triggered, qApp, []() { crash::doAbort(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashAbortThreaded); - connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::doAbort(); }); }); + connect(action, &QAction::triggered, qApp, []() { std::thread(crash::doAbort).join(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNullDereference); connect(action, &QAction::triggered, qApp, []() { crash::nullDeref(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNullDereferenceThreaded); - connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::nullDeref(); }); }); + connect(action, &QAction::triggered, qApp, []() { std::thread(crash::nullDeref).join(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashOutOfBoundsVectorAccess); connect(action, &QAction::triggered, qApp, []() { crash::outOfBoundsVectorCrash(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashOutOfBoundsVectorAccessThreaded); - connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::outOfBoundsVectorCrash(); }); }); + connect(action, &QAction::triggered, qApp, []() { std::thread(crash::outOfBoundsVectorCrash).join(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNewFault); connect(action, &QAction::triggered, qApp, []() { crash::newFault(); }); action = addActionToQMenuAndActionHash(crashMenu, MenuOption::CrashNewFaultThreaded); - connect(action, &QAction::triggered, qApp, []() { std::thread([]() { crash::newFault(); }); }); + connect(action, &QAction::triggered, qApp, []() { std::thread(crash::newFault).join(); }); // Developer > Log... addActionToQMenuAndActionHash(developerMenu, MenuOption::Log, Qt::CTRL | Qt::SHIFT | Qt::Key_L, diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 0803e380f2..e27e2d6d08 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -578,9 +578,10 @@ void LimitedNodeList::reset() { // we need to make sure any socket connections are gone so wait on that here _nodeSocket.clearConnections(); + _connectionIDs.clear(); } -bool LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { +bool LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID, ConnectionID newConnectionID) { QReadLocker readLocker(&_nodeMutex); NodeHash::iterator it = _nodeHash.find(nodeUUID); @@ -594,7 +595,7 @@ bool LimitedNodeList::killNodeWithUUID(const QUuid& nodeUUID) { _nodeHash.unsafe_erase(it); } - handleNodeKill(matchingNode); + handleNodeKill(matchingNode, newConnectionID); return true; } @@ -609,7 +610,7 @@ void LimitedNodeList::processKillNode(ReceivedMessage& message) { killNodeWithUUID(nodeUUID); } -void LimitedNodeList::handleNodeKill(const SharedNodePointer& node) { +void LimitedNodeList::handleNodeKill(const SharedNodePointer& node, ConnectionID nextConnectionID) { qCDebug(networking) << "Killed" << *node; node->stopPingTimer(); emit nodeKilled(node); @@ -617,6 +618,15 @@ void LimitedNodeList::handleNodeKill(const SharedNodePointer& node) { if (auto activeSocket = node->getActiveSocket()) { _nodeSocket.cleanupConnection(*activeSocket); } + + auto it = _connectionIDs.find(node->getUUID()); + if (it != _connectionIDs.end()) { + if (nextConnectionID == NULL_CONNECTION_ID) { + it->second++; + } else { + it->second = nextConnectionID; + } + } } SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType, @@ -638,6 +648,11 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t return matchingNode; } else { + auto it = _connectionIDs.find(uuid); + if (it == _connectionIDs.end()) { + _connectionIDs[uuid] = INITIAL_CONNECTION_ID; + } + // we didn't have this node, so add them Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket); newNode->setIsReplicated(isReplicated); @@ -712,13 +727,13 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t } } -std::unique_ptr LimitedNodeList::constructPingPacket(PingType_t pingType) { - int packetSize = sizeof(PingType_t) + sizeof(quint64); +std::unique_ptr LimitedNodeList::constructPingPacket(const QUuid& nodeId, PingType_t pingType) { + int packetSize = sizeof(PingType_t) + sizeof(quint64) + sizeof(int64_t); auto pingPacket = NLPacket::create(PacketType::Ping, packetSize); - pingPacket->writePrimitive(pingType); pingPacket->writePrimitive(usecTimestampNow()); + pingPacket->writePrimitive(_connectionIDs[nodeId]); return pingPacket; } diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 7165b3dd63..7ec3a41450 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -66,6 +66,10 @@ const QHostAddress DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME = QHostAddress::Lo const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username"; +using ConnectionID = int64_t; +const ConnectionID NULL_CONNECTION_ID { -1 }; +const ConnectionID INITIAL_CONNECTION_ID { 0 }; + typedef std::pair UUIDNodePair; typedef tbb::concurrent_unordered_map NodeHash; @@ -180,7 +184,7 @@ public: void getPacketStats(float& packetsInPerSecond, float& bytesInPerSecond, float& packetsOutPerSecond, float& bytesOutPerSecond); void resetPacketStats(); - std::unique_ptr constructPingPacket(PingType_t pingType = PingType::Agnostic); + std::unique_ptr constructPingPacket(const QUuid& nodeId, PingType_t pingType = PingType::Agnostic); std::unique_ptr constructPingReplyPacket(ReceivedMessage& message); static std::unique_ptr constructICEPingPacket(PingType_t pingType, const QUuid& iceID); @@ -319,7 +323,7 @@ public slots: void startSTUNPublicSocketUpdate(); virtual void sendSTUNRequest(); - bool killNodeWithUUID(const QUuid& nodeUUID); + bool killNodeWithUUID(const QUuid& nodeUUID, ConnectionID newConnectionID = NULL_CONNECTION_ID); signals: void dataSent(quint8 channelType, int bytes); @@ -371,7 +375,7 @@ protected: bool packetSourceAndHashMatchAndTrackBandwidth(const udt::Packet& packet, Node* sourceNode = nullptr); void processSTUNResponse(std::unique_ptr packet); - void handleNodeKill(const SharedNodePointer& node); + void handleNodeKill(const SharedNodePointer& node, ConnectionID newConnectionID = NULL_CONNECTION_ID); void stopInitialSTUNUpdate(bool success); @@ -418,6 +422,7 @@ protected: } } + std::unordered_map _connectionIDs; private slots: void flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 13931be2ac..172b016e13 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -214,6 +214,20 @@ void NodeList::processPingPacket(QSharedPointer message, Shared sendingNode->setSymmetricSocket(senderSockAddr); } } + + int64_t connectionID; + + message->readPrimitive(&connectionID); + + auto it = _connectionIDs.find(sendingNode->getUUID()); + if (it != _connectionIDs.end()) { + if (connectionID > it->second) { + qDebug() << "Received a ping packet with a larger connection id (" << connectionID << ">" << it->second << ") from " + << sendingNode->getUUID(); + killNodeWithUUID(sendingNode->getUUID(), connectionID); + } + } + } void NodeList::processPingReplyPacket(QSharedPointer message, SharedNodePointer sendingNode) { @@ -705,16 +719,18 @@ void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) { if (node->getConnectionAttempts() > 0 && node->getConnectionAttempts() % NUM_DEBUG_CONNECTION_ATTEMPTS == 0) { qCDebug(networking) << "No response to UDP hole punch pings for node" << node->getUUID() << "in last second."; } + + auto nodeID = node->getUUID(); // send the ping packet to the local and public sockets for this node - auto localPingPacket = constructPingPacket(PingType::Local); + auto localPingPacket = constructPingPacket(nodeID, PingType::Local); sendPacket(std::move(localPingPacket), *node, node->getLocalSocket()); - auto publicPingPacket = constructPingPacket(PingType::Public); + auto publicPingPacket = constructPingPacket(nodeID, PingType::Public); sendPacket(std::move(publicPingPacket), *node, node->getPublicSocket()); if (!node->getSymmetricSocket().isNull()) { - auto symmetricPingPacket = constructPingPacket(PingType::Symmetric); + auto symmetricPingPacket = constructPingPacket(nodeID, PingType::Symmetric); sendPacket(std::move(symmetricPingPacket), *node, node->getSymmetricSocket()); } @@ -784,7 +800,7 @@ void NodeList::sendKeepAlivePings() { auto type = node->getType(); return !node->isUpstream() && _nodeTypesOfInterest.contains(type) && !NodeType::isDownstream(type); }, [&](const SharedNodePointer& node) { - sendPacket(constructPingPacket(), *node); + sendPacket(constructPingPacket(node->getUUID()), *node); }); } diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 18e4593c91..8b6de7da11 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -84,7 +84,8 @@ void ThreadedAssignment::commonInit(const QString& targetName, NodeType_t nodeTy _domainServerTimer.start(); // start sending stats packet once we connect to the domain - connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), &_statsTimer, SLOT(start())); + connect(&nodeList->getDomainHandler(), &DomainHandler::connectedToDomain, + &_statsTimer, static_cast(&QTimer::start)); // stop sending stats if we disconnect connect(&nodeList->getDomainHandler(), &DomainHandler::disconnectedFromDomain, &_statsTimer, &QTimer::stop); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index a83924ee58..11b2c516f8 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -75,6 +75,8 @@ PacketVersion versionForPacketType(PacketType packetType) { return static_cast(IcePingVersion::SendICEPeerID); case PacketType::DomainSettings: return 18; // replace min_avatar_scale and max_avatar_scale with min_avatar_height and max_avatar_height + case PacketType::Ping: + return static_cast(PingVersion::IncludeConnectionID); default: return 17; } diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 09fd31a41e..091fcb1091 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -322,4 +322,8 @@ enum class IcePingVersion : PacketVersion { SendICEPeerID = 18 }; +enum class PingVersion : PacketVersion { + IncludeConnectionID = 18 +}; + #endif // hifi_PacketHeaders_h diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index f484f32fdf..5abeb022aa 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -114,6 +114,11 @@ btConvexHullShape* createConvexHull(const ShapeInfo::PointList& points) { minCorner = glm::min(minCorner, points[i]); } center /= (float)(points.size()); + if (glm::any(glm::isnan(center))) { + // don't feed garbage to Bullet + assert(false); // crash here in DEBUG so we can investigate source of bad input + return nullptr; + } float margin = hull->getMargin(); @@ -265,7 +270,7 @@ btTriangleIndexVertexArray* createStaticMeshArray(const ShapeInfo& info) { } const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { - btCollisionShape* shape = NULL; + btCollisionShape* shape = nullptr; int type = info.getType(); switch(type) { case SHAPE_TYPE_BOX: { diff --git a/libraries/render/src/render/Item.cpp b/libraries/render/src/render/Item.cpp index ed052adf6e..9c5efb9fa7 100644 --- a/libraries/render/src/render/Item.cpp +++ b/libraries/render/src/render/Item.cpp @@ -118,9 +118,15 @@ uint32_t Item::fetchMetaSubItemBounds(ItemBounds& subItemBounds, Scene& scene) c auto numSubs = fetchMetaSubItems(subItems); for (auto id : subItems) { - auto& item = scene.getItem(id); - if (item.exist()) { - subItemBounds.emplace_back(id, item.getBound()); + // TODO: Adding an extra check here even thought we shouldn't have too. + // We have cases when the id returned by fetchMetaSubItems is not allocated + if (scene.isAllocatedID(id)) { + auto& item = scene.getItem(id); + if (item.exist()) { + subItemBounds.emplace_back(id, item.getBound()); + } else { + numSubs--; + } } else { numSubs--; }