mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 11:22:24 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into unit-scaled-address-bar
This commit is contained in:
commit
a5fc0d92a2
41 changed files with 770 additions and 216 deletions
|
@ -863,15 +863,6 @@
|
||||||
"help": "The path to the directory assets are stored in.<br/>If this path is relative, it will be relative to the application data directory.<br/>If you change this path you will need to manually copy any existing assets from the previous directory.",
|
"help": "The path to the directory assets are stored in.<br/>If this path is relative, it will be relative to the application data directory.<br/>If you change this path you will need to manually copy any existing assets from the previous directory.",
|
||||||
"default": "",
|
"default": "",
|
||||||
"advanced": true
|
"advanced": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "max_bandwidth",
|
|
||||||
"type": "double",
|
|
||||||
"label": "Max Bandwidth Per User",
|
|
||||||
"help": "The maximum upstream bandwidth each user can use (in Mb/s).",
|
|
||||||
"placeholder": "10.0",
|
|
||||||
"default": "",
|
|
||||||
"advanced": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,8 +41,10 @@ endif ()
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
set(PLATFORM_QT_COMPONENTS AndroidExtras)
|
set(PLATFORM_QT_COMPONENTS AndroidExtras)
|
||||||
|
set(PLATFORM_QT_LIBRARIES Qt5::AndroidExtras)
|
||||||
else ()
|
else ()
|
||||||
set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets)
|
set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets)
|
||||||
|
set(PLATFORM_QT_LIBRARIES Qt5::WebEngine Qt5::WebEngineWidgets)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
find_package(
|
find_package(
|
||||||
|
@ -244,7 +246,8 @@ target_link_libraries(
|
||||||
${TARGET_NAME}
|
${TARGET_NAME}
|
||||||
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
|
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
|
||||||
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
|
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
|
||||||
Qt5::WebChannel Qt5::WebEngine
|
Qt5::WebChannel Qt5::WebEngine
|
||||||
|
${PLATFORM_QT_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
|
|
|
@ -279,6 +279,7 @@ Window {
|
||||||
verticalCenter: backgroundImage.verticalCenter;
|
verticalCenter: backgroundImage.verticalCenter;
|
||||||
horizontalCenter: scroll.horizontalCenter;
|
horizontalCenter: scroll.horizontalCenter;
|
||||||
}
|
}
|
||||||
|
z: 100
|
||||||
}
|
}
|
||||||
|
|
||||||
HifiControls.Keyboard {
|
HifiControls.Keyboard {
|
||||||
|
|
|
@ -29,8 +29,9 @@ Item {
|
||||||
readonly property int maxHeight: 720
|
readonly property int maxHeight: 720
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
var targetWidth = Math.max(titleWidth, additionalTextContainer.contentWidth)
|
var targetWidth = Math.max(titleWidth, Math.max(additionalTextContainer.contentWidth,
|
||||||
var targetHeight = 4 * hifi.dimensions.contentSpacing.y + buttons.height + additionalTextContainer.height
|
termsContainer.contentWidth))
|
||||||
|
var targetHeight = 5 * hifi.dimensions.contentSpacing.y + buttons.height + additionalTextContainer.height + termsContainer.height
|
||||||
|
|
||||||
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
|
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
|
||||||
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||||
|
@ -43,7 +44,7 @@ Item {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
margins: 0
|
margins: 0
|
||||||
topMargin: 3 * hifi.dimensions.contentSpacing.y
|
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||||
}
|
}
|
||||||
spacing: hifi.dimensions.contentSpacing.x
|
spacing: hifi.dimensions.contentSpacing.x
|
||||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||||
|
@ -91,6 +92,25 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InfoItem {
|
||||||
|
id: termsContainer
|
||||||
|
anchors {
|
||||||
|
top: additionalTextContainer.bottom
|
||||||
|
left: parent.left
|
||||||
|
margins: 0
|
||||||
|
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
||||||
|
}
|
||||||
|
|
||||||
|
text: qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
color: hifi.colors.baseGrayHighlight
|
||||||
|
lineHeight: 1
|
||||||
|
lineHeightMode: Text.ProportionalHeight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
onLinkActivated: loginDialog.openUrl(link)
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
root.title = qsTr("Complete Your Profile")
|
root.title = qsTr("Complete Your Profile")
|
||||||
root.iconText = "<"
|
root.iconText = "<"
|
||||||
|
|
|
@ -42,8 +42,14 @@ Item {
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
var targetWidth = Math.max(titleWidth, form.contentWidth);
|
var targetWidth = Math.max(titleWidth, form.contentWidth);
|
||||||
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height
|
var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height +
|
||||||
+ 4 * hifi.dimensions.contentSpacing.y + form.height + hifi.dimensions.contentSpacing.y + buttons.height;
|
4 * hifi.dimensions.contentSpacing.y + form.height +
|
||||||
|
hifi.dimensions.contentSpacing.y + buttons.height;
|
||||||
|
|
||||||
|
if (additionalInformation.visible) {
|
||||||
|
targetWidth = Math.max(targetWidth, additionalInformation.width);
|
||||||
|
targetHeight += hifi.dimensions.contentSpacing.y + additionalInformation.height
|
||||||
|
}
|
||||||
|
|
||||||
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth));
|
||||||
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||||
|
@ -136,6 +142,25 @@ Item {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InfoItem {
|
||||||
|
id: additionalInformation
|
||||||
|
anchors {
|
||||||
|
top: form.bottom
|
||||||
|
left: parent.left
|
||||||
|
margins: 0
|
||||||
|
topMargin: hifi.dimensions.contentSpacing.y
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: loginDialog.isSteamRunning()
|
||||||
|
|
||||||
|
text: qsTr("Your steam account informations will not be exposed to other users.")
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
color: hifi.colors.baseGrayHighlight
|
||||||
|
lineHeight: 1
|
||||||
|
lineHeightMode: Text.ProportionalHeight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
// Override ScrollingWindow's keyboard that would be at very bottom of dialog.
|
// Override ScrollingWindow's keyboard that would be at very bottom of dialog.
|
||||||
Keyboard {
|
Keyboard {
|
||||||
raised: keyboardEnabled && keyboardRaised
|
raised: keyboardEnabled && keyboardRaised
|
||||||
|
|
|
@ -27,6 +27,13 @@ Item {
|
||||||
loginDialog.createAccountFromStream(textField.text)
|
loginDialog.createAccountFromStream(textField.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
property bool keyboardEnabled: false
|
||||||
|
property bool keyboardRaised: false
|
||||||
|
property bool punctuationMode: false
|
||||||
|
|
||||||
|
onKeyboardRaisedChanged: d.resize();
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
id: d
|
id: d
|
||||||
readonly property int minWidth: 480
|
readonly property int minWidth: 480
|
||||||
|
@ -35,15 +42,16 @@ Item {
|
||||||
readonly property int maxHeight: 720
|
readonly property int maxHeight: 720
|
||||||
|
|
||||||
function resize() {
|
function resize() {
|
||||||
var targetWidth = Math.max(titleWidth, Math.max(mainTextContainer.contentWidth,
|
var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth)
|
||||||
termsContainer.contentWidth))
|
|
||||||
var targetHeight = mainTextContainer.height +
|
var targetHeight = mainTextContainer.height +
|
||||||
2 * hifi.dimensions.contentSpacing.y + textField.height +
|
hifi.dimensions.contentSpacing.y + textField.height +
|
||||||
5 * hifi.dimensions.contentSpacing.y + termsContainer.height +
|
hifi.dimensions.contentSpacing.y + buttons.height
|
||||||
1 * hifi.dimensions.contentSpacing.y + buttons.height
|
|
||||||
|
|
||||||
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
|
root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth))
|
||||||
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight))
|
||||||
|
+ (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y)
|
||||||
|
|
||||||
|
height = root.height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,39 +79,32 @@ Item {
|
||||||
top: mainTextContainer.bottom
|
top: mainTextContainer.bottom
|
||||||
left: parent.left
|
left: parent.left
|
||||||
margins: 0
|
margins: 0
|
||||||
topMargin: 2 * hifi.dimensions.contentSpacing.y
|
topMargin: hifi.dimensions.contentSpacing.y
|
||||||
}
|
}
|
||||||
width: 250
|
width: 250
|
||||||
|
|
||||||
placeholderText: "Choose your own"
|
placeholderText: "Choose your own"
|
||||||
}
|
}
|
||||||
|
|
||||||
InfoItem {
|
// Override ScrollingWindow's keyboard that would be at very bottom of dialog.
|
||||||
id: termsContainer
|
Keyboard {
|
||||||
|
raised: keyboardEnabled && keyboardRaised
|
||||||
|
numeric: punctuationMode
|
||||||
anchors {
|
anchors {
|
||||||
top: textField.bottom
|
|
||||||
left: parent.left
|
left: parent.left
|
||||||
margins: 0
|
right: parent.right
|
||||||
topMargin: 3 * hifi.dimensions.contentSpacing.y
|
bottom: buttons.top
|
||||||
|
bottomMargin: keyboardRaised ? 2 * hifi.dimensions.contentSpacing.y : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
text: qsTr("By creating this user profile, you agree to <a href='https://highfidelity.com/terms'>High Fidelity's Terms of Service</a>")
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
color: hifi.colors.baseGrayHighlight
|
|
||||||
lineHeight: 1
|
|
||||||
lineHeightMode: Text.ProportionalHeight
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
|
|
||||||
onLinkActivated: loginDialog.openUrl(link)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: buttons
|
id: buttons
|
||||||
anchors {
|
anchors {
|
||||||
top: termsContainer.bottom
|
bottom: parent.bottom
|
||||||
right: parent.right
|
right: parent.right
|
||||||
margins: 0
|
margins: 0
|
||||||
topMargin: 1 * hifi.dimensions.contentSpacing.y
|
topMargin: hifi.dimensions.contentSpacing.y
|
||||||
}
|
}
|
||||||
spacing: hifi.dimensions.contentSpacing.x
|
spacing: hifi.dimensions.contentSpacing.x
|
||||||
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
onHeightChanged: d.resize(); onWidthChanged: d.resize();
|
||||||
|
@ -130,8 +131,10 @@ Item {
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
root.title = qsTr("Complete Your Profile")
|
root.title = qsTr("Complete Your Profile")
|
||||||
root.iconText = "<"
|
root.iconText = "<"
|
||||||
|
keyboardEnabled = HMD.active;
|
||||||
d.resize();
|
d.resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: loginDialog
|
target: loginDialog
|
||||||
onHandleCreateCompleted: {
|
onHandleCreateCompleted: {
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
|
|
||||||
import QtQuick 2.5
|
import QtQuick 2.5
|
||||||
import QtWebEngine 1.2
|
import QtWebEngine 1.2
|
||||||
|
import HFWebEngineProfile 1.0
|
||||||
|
|
||||||
WebEngineView {
|
WebEngineView {
|
||||||
id: root
|
id: root
|
||||||
property var newUrl;
|
|
||||||
|
|
||||||
profile: desktop.browserProfile
|
profile: desktop.browserProfile
|
||||||
|
|
||||||
|
@ -25,28 +25,6 @@ WebEngineView {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
|
|
||||||
Timer {
|
|
||||||
id: urlReplacementTimer
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
interval: 50
|
|
||||||
onTriggered: url = newUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
onUrlChanged: {
|
|
||||||
var originalUrl = url.toString();
|
|
||||||
newUrl = urlHandler.fixupUrl(originalUrl).toString();
|
|
||||||
if (newUrl !== originalUrl) {
|
|
||||||
root.stop();
|
|
||||||
if (urlReplacementTimer.running) {
|
|
||||||
console.warn("Replacement timer already running");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
urlReplacementTimer.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoadingChanged: {
|
onLoadingChanged: {
|
||||||
// Required to support clicking on "hifi://" links
|
// Required to support clicking on "hifi://" links
|
||||||
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import QtQuick 2.5
|
||||||
import QtWebEngine 1.1
|
import QtWebEngine 1.1
|
||||||
import QtWebChannel 1.0
|
import QtWebChannel 1.0
|
||||||
import "../controls-uit" as HiFiControls
|
import "../controls-uit" as HiFiControls
|
||||||
|
import HFWebEngineProfile 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property alias url: root.url
|
property alias url: root.url
|
||||||
|
@ -31,6 +32,11 @@ Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
|
height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height
|
||||||
|
|
||||||
|
profile: HFWebEngineProfile {
|
||||||
|
id: webviewProfile
|
||||||
|
storageName: "qmlWebEngine"
|
||||||
|
}
|
||||||
|
|
||||||
// creates a global EventBridge object.
|
// creates a global EventBridge object.
|
||||||
WebEngineScript {
|
WebEngineScript {
|
||||||
id: createGlobalEventBridge
|
id: createGlobalEventBridge
|
||||||
|
@ -62,28 +68,6 @@ Item {
|
||||||
root.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
|
root.profile.httpUserAgent = "Mozilla/5.0 Chrome (HighFidelityInterface)";
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME hack to get the URL with the auth token included. Remove when we move to Qt 5.6
|
|
||||||
Timer {
|
|
||||||
id: urlReplacementTimer
|
|
||||||
running: false
|
|
||||||
repeat: false
|
|
||||||
interval: 50
|
|
||||||
onTriggered: url = root.newUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
onUrlChanged: {
|
|
||||||
var originalUrl = url.toString();
|
|
||||||
root.newUrl = urlHandler.fixupUrl(originalUrl).toString();
|
|
||||||
if (root.newUrl !== originalUrl) {
|
|
||||||
root.stop();
|
|
||||||
if (urlReplacementTimer.running) {
|
|
||||||
console.warn("Replacement timer already running");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
urlReplacementTimer.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onFeaturePermissionRequested: {
|
onFeaturePermissionRequested: {
|
||||||
grantFeaturePermission(securityOrigin, feature, true);
|
grantFeaturePermission(securityOrigin, feature, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ FocusScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onHeightChanged: d.handleSizeChanged();
|
onHeightChanged: d.handleSizeChanged();
|
||||||
|
|
||||||
onWidthChanged: d.handleSizeChanged();
|
onWidthChanged: d.handleSizeChanged();
|
||||||
|
|
|
@ -2,6 +2,7 @@ import QtQuick 2.5
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
import QtWebEngine 1.1;
|
import QtWebEngine 1.1;
|
||||||
import Qt.labs.settings 1.0
|
import Qt.labs.settings 1.0
|
||||||
|
import HFWebEngineProfile 1.0
|
||||||
|
|
||||||
import "../desktop" as OriginalDesktop
|
import "../desktop" as OriginalDesktop
|
||||||
import ".."
|
import ".."
|
||||||
|
@ -20,17 +21,14 @@ OriginalDesktop.Desktop {
|
||||||
onEntered: ApplicationCompositor.reticleOverDesktop = true
|
onEntered: ApplicationCompositor.reticleOverDesktop = true
|
||||||
onExited: ApplicationCompositor.reticleOverDesktop = false
|
onExited: ApplicationCompositor.reticleOverDesktop = false
|
||||||
acceptedButtons: Qt.NoButton
|
acceptedButtons: Qt.NoButton
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The tool window, one instance
|
// The tool window, one instance
|
||||||
property alias toolWindow: toolWindow
|
property alias toolWindow: toolWindow
|
||||||
ToolWindow { id: toolWindow }
|
ToolWindow { id: toolWindow }
|
||||||
|
|
||||||
property var browserProfile: WebEngineProfile {
|
property var browserProfile: HFWebEngineProfile {
|
||||||
id: webviewProfile
|
id: webviewProfile
|
||||||
httpUserAgent: "Chrome/48.0 (HighFidelityInterface)"
|
|
||||||
storageName: "qmlWebEngine"
|
storageName: "qmlWebEngine"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,5 +125,3 @@ OriginalDesktop.Desktop {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
#include <QtQml/QQmlEngine>
|
#include <QtQml/QQmlEngine>
|
||||||
#include <QtQuick/QQuickWindow>
|
#include <QtQuick/QQuickWindow>
|
||||||
|
|
||||||
|
#include <QtWebEngineWidgets/QWebEngineProfile>
|
||||||
|
|
||||||
#include <QtWidgets/QDesktopWidget>
|
#include <QtWidgets/QDesktopWidget>
|
||||||
#include <QtWidgets/QMessageBox>
|
#include <QtWidgets/QMessageBox>
|
||||||
|
|
||||||
|
@ -126,6 +128,7 @@
|
||||||
#include "InterfaceLogging.h"
|
#include "InterfaceLogging.h"
|
||||||
#include "LODManager.h"
|
#include "LODManager.h"
|
||||||
#include "ModelPackager.h"
|
#include "ModelPackager.h"
|
||||||
|
#include "networking/HFWebEngineProfile.h"
|
||||||
#include "scripting/AccountScriptingInterface.h"
|
#include "scripting/AccountScriptingInterface.h"
|
||||||
#include "scripting/AssetMappingsScriptingInterface.h"
|
#include "scripting/AssetMappingsScriptingInterface.h"
|
||||||
#include "scripting/AudioDeviceScriptingInterface.h"
|
#include "scripting/AudioDeviceScriptingInterface.h"
|
||||||
|
@ -1698,6 +1701,7 @@ void Application::initializeUi() {
|
||||||
UpdateDialog::registerType();
|
UpdateDialog::registerType();
|
||||||
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
qmlRegisterType<Preference>("Hifi", 1, 0, "Preference");
|
||||||
|
|
||||||
|
qmlRegisterType<HFWebEngineProfile>("HFWebEngineProfile", 1, 0, "HFWebEngineProfile");
|
||||||
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||||
offscreenUi->create(_glWidget->qglContext());
|
offscreenUi->create(_glWidget->qglContext());
|
||||||
|
|
|
@ -603,14 +603,14 @@ void Avatar::fixupModelsInScene() {
|
||||||
_skeletonModel->removeFromScene(scene, pendingChanges);
|
_skeletonModel->removeFromScene(scene, pendingChanges);
|
||||||
_skeletonModel->addToScene(scene, pendingChanges);
|
_skeletonModel->addToScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
for (auto& attachmentModel : _attachmentModels) {
|
for (auto attachmentModel : _attachmentModels) {
|
||||||
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
|
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
|
||||||
attachmentModel->removeFromScene(scene, pendingChanges);
|
attachmentModel->removeFromScene(scene, pendingChanges);
|
||||||
attachmentModel->addToScene(scene, pendingChanges);
|
attachmentModel->addToScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& attachmentModelToRemove : _attachmentsToRemove) {
|
for (auto attachmentModelToRemove : _attachmentsToRemove) {
|
||||||
attachmentModelToRemove->removeFromScene(scene, pendingChanges);
|
attachmentModelToRemove->removeFromScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
_attachmentsToDelete.insert(_attachmentsToDelete.end(), _attachmentsToRemove.begin(), _attachmentsToRemove.end());
|
_attachmentsToDelete.insert(_attachmentsToDelete.end(), _attachmentsToRemove.begin(), _attachmentsToRemove.end());
|
||||||
|
|
27
interface/src/networking/HFWebEngineProfile.cpp
Normal file
27
interface/src/networking/HFWebEngineProfile.cpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// HFWebEngineProfile.cpp
|
||||||
|
// interface/src/networking
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2016-10-17.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "HFWebEngineProfile.h"
|
||||||
|
|
||||||
|
#include "HFWebEngineRequestInterceptor.h"
|
||||||
|
|
||||||
|
static const QString QML_WEB_ENGINE_STORAGE_NAME = "qmlWebEngine";
|
||||||
|
|
||||||
|
HFWebEngineProfile::HFWebEngineProfile(QObject* parent) :
|
||||||
|
QQuickWebEngineProfile(parent)
|
||||||
|
{
|
||||||
|
static const QString WEB_ENGINE_USER_AGENT = "Chrome/48.0 (HighFidelityInterface)";
|
||||||
|
setHttpUserAgent(WEB_ENGINE_USER_AGENT);
|
||||||
|
|
||||||
|
// we use the HFWebEngineRequestInterceptor to make sure that web requests are authenticated for the interface user
|
||||||
|
auto requestInterceptor = new HFWebEngineRequestInterceptor(this);
|
||||||
|
setRequestInterceptor(requestInterceptor);
|
||||||
|
}
|
25
interface/src/networking/HFWebEngineProfile.h
Normal file
25
interface/src/networking/HFWebEngineProfile.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// HFWebEngineProfile.h
|
||||||
|
// interface/src/networking
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2016-10-17.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_HFWebEngineProfile_h
|
||||||
|
#define hifi_HFWebEngineProfile_h
|
||||||
|
|
||||||
|
#include <QtWebEngine/QQuickWebEngineProfile>
|
||||||
|
|
||||||
|
class HFWebEngineProfile : public QQuickWebEngineProfile {
|
||||||
|
public:
|
||||||
|
HFWebEngineProfile(QObject* parent = Q_NULLPTR);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // hifi_HFWebEngineProfile_h
|
40
interface/src/networking/HFWebEngineRequestInterceptor.cpp
Normal file
40
interface/src/networking/HFWebEngineRequestInterceptor.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
// HFWebEngineRequestInterceptor.cpp
|
||||||
|
// interface/src/networking
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2016-10-14.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "HFWebEngineRequestInterceptor.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
#include <AccountManager.h>
|
||||||
|
|
||||||
|
bool isAuthableHighFidelityURL(const QUrl& url) {
|
||||||
|
static const QStringList HF_HOSTS = {
|
||||||
|
"highfidelity.com", "highfidelity.io",
|
||||||
|
"metaverse.highfidelity.com", "metaverse.highfidelity.io"
|
||||||
|
};
|
||||||
|
|
||||||
|
return url.scheme() == "https" && HF_HOSTS.contains(url.host());
|
||||||
|
}
|
||||||
|
|
||||||
|
void HFWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo& info) {
|
||||||
|
// check if this is a request to a highfidelity URL
|
||||||
|
if (isAuthableHighFidelityURL(info.requestUrl())) {
|
||||||
|
// if we have an access token, add it to the right HTTP header for authorization
|
||||||
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
|
|
||||||
|
if (accountManager->hasValidAccessToken()) {
|
||||||
|
static const QString OAUTH_AUTHORIZATION_HEADER = "Authorization";
|
||||||
|
|
||||||
|
QString bearerTokenString = "Bearer " + accountManager->getAccountInfo().getAccessToken().token;
|
||||||
|
info.setHttpHeader(OAUTH_AUTHORIZATION_HEADER.toLocal8Bit(), bearerTokenString.toLocal8Bit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
interface/src/networking/HFWebEngineRequestInterceptor.h
Normal file
26
interface/src/networking/HFWebEngineRequestInterceptor.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// HFWebEngineRequestInterceptor.h
|
||||||
|
// interface/src/networking
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2016-10-14.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_HFWebEngineRequestInterceptor_h
|
||||||
|
#define hifi_HFWebEngineRequestInterceptor_h
|
||||||
|
|
||||||
|
#include <QWebEngineUrlRequestInterceptor>
|
||||||
|
|
||||||
|
class HFWebEngineRequestInterceptor : public QWebEngineUrlRequestInterceptor {
|
||||||
|
public:
|
||||||
|
HFWebEngineRequestInterceptor(QObject* parent) : QWebEngineUrlRequestInterceptor(parent) {};
|
||||||
|
|
||||||
|
virtual void interceptRequest(QWebEngineUrlRequestInfo& info) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_HFWebEngineRequestInterceptor_h
|
|
@ -40,19 +40,6 @@
|
||||||
#include "TextureRecycler.h"
|
#include "TextureRecycler.h"
|
||||||
#include "Context.h"
|
#include "Context.h"
|
||||||
|
|
||||||
QString fixupHifiUrl(const QString& urlString) {
|
|
||||||
static const QString ACCESS_TOKEN_PARAMETER = "access_token";
|
|
||||||
static const QString ALLOWED_HOST = "metaverse.highfidelity.com";
|
|
||||||
QUrl url(urlString);
|
|
||||||
QUrlQuery query(url);
|
|
||||||
if (url.host() == ALLOWED_HOST && query.allQueryItemValues(ACCESS_TOKEN_PARAMETER).empty()) {
|
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
|
||||||
query.addQueryItem(ACCESS_TOKEN_PARAMETER, accountManager->getAccountInfo().getAccessToken().token);
|
|
||||||
url.setQuery(query.query());
|
|
||||||
return url.toString();
|
|
||||||
}
|
|
||||||
return urlString;
|
|
||||||
}
|
|
||||||
|
|
||||||
class UrlHandler : public QObject {
|
class UrlHandler : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -66,11 +53,6 @@ public:
|
||||||
static auto handler = dynamic_cast<AbstractUriHandler*>(qApp);
|
static auto handler = dynamic_cast<AbstractUriHandler*>(qApp);
|
||||||
return handler->acceptURL(url);
|
return handler->acceptURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME hack for authentication, remove when we migrate to Qt 5.6
|
|
||||||
Q_INVOKABLE QString fixupUrl(const QString& originalUrl) {
|
|
||||||
return fixupHifiUrl(originalUrl);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Time between receiving a request to render the offscreen UI actually triggering
|
// Time between receiving a request to render the offscreen UI actually triggering
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
#include <QtCore/QIODevice>
|
#include <QtCore/QIODevice>
|
||||||
|
|
||||||
|
#include <PortableHighResolutionClock.h>
|
||||||
|
|
||||||
#include "../HifiSockAddr.h"
|
#include "../HifiSockAddr.h"
|
||||||
#include "Constants.h"
|
#include "Constants.h"
|
||||||
|
|
||||||
|
@ -80,6 +82,9 @@ public:
|
||||||
|
|
||||||
qint64 writeString(const QString& string);
|
qint64 writeString(const QString& string);
|
||||||
QString readString();
|
QString readString();
|
||||||
|
|
||||||
|
void setReceiveTime(p_high_resolution_clock::time_point receiveTime) { _receiveTime = receiveTime; }
|
||||||
|
p_high_resolution_clock::time_point getReceiveTime() const { return _receiveTime; }
|
||||||
|
|
||||||
template<typename T> qint64 peekPrimitive(T* data);
|
template<typename T> qint64 peekPrimitive(T* data);
|
||||||
template<typename T> qint64 readPrimitive(T* data);
|
template<typename T> qint64 readPrimitive(T* data);
|
||||||
|
@ -108,6 +113,8 @@ protected:
|
||||||
qint64 _payloadSize = 0; // How much of the payload is actually used
|
qint64 _payloadSize = 0; // How much of the payload is actually used
|
||||||
|
|
||||||
HifiSockAddr _senderSockAddr; // sender address for packet (only used on receiving end)
|
HifiSockAddr _senderSockAddr; // sender address for packet (only used on receiving end)
|
||||||
|
|
||||||
|
p_high_resolution_clock::time_point _receiveTime; // captures the time the packet received (only used on receiving end)
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T> qint64 BasePacket::peekPrimitive(T* data) {
|
template<typename T> qint64 BasePacket::peekPrimitive(T* data) {
|
||||||
|
|
|
@ -45,11 +45,11 @@ DefaultCC::DefaultCC() :
|
||||||
{
|
{
|
||||||
_mss = udt::MAX_PACKET_SIZE_WITH_UDP_HEADER;
|
_mss = udt::MAX_PACKET_SIZE_WITH_UDP_HEADER;
|
||||||
|
|
||||||
_congestionWindowSize = 16.0;
|
_congestionWindowSize = 16;
|
||||||
setPacketSendPeriod(1.0);
|
setPacketSendPeriod(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefaultCC::onACK(SequenceNumber ackNum) {
|
bool DefaultCC::onACK(SequenceNumber ackNum, p_high_resolution_clock::time_point receiveTime) {
|
||||||
double increase = 0;
|
double increase = 0;
|
||||||
|
|
||||||
// Note from UDT original code:
|
// Note from UDT original code:
|
||||||
|
@ -61,7 +61,7 @@ void DefaultCC::onACK(SequenceNumber ackNum) {
|
||||||
// we will only adjust once per sync interval so check that it has been at least that long now
|
// we will only adjust once per sync interval so check that it has been at least that long now
|
||||||
auto now = p_high_resolution_clock::now();
|
auto now = p_high_resolution_clock::now();
|
||||||
if (duration_cast<microseconds>(now - _lastRCTime).count() < synInterval()) {
|
if (duration_cast<microseconds>(now - _lastRCTime).count() < synInterval()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// our last rate increase time is now
|
// our last rate increase time is now
|
||||||
|
@ -93,13 +93,13 @@ void DefaultCC::onACK(SequenceNumber ackNum) {
|
||||||
|
|
||||||
// during slow start we perform no rate increases
|
// during slow start we perform no rate increases
|
||||||
if (_slowStart) {
|
if (_slowStart) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if loss has happened since the last rate increase we do not perform another increase
|
// if loss has happened since the last rate increase we do not perform another increase
|
||||||
if (_loss) {
|
if (_loss) {
|
||||||
_loss = false;
|
_loss = false;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
double capacitySpeedDelta = (_bandwidth - USECS_PER_SECOND / _packetSendPeriod);
|
double capacitySpeedDelta = (_bandwidth - USECS_PER_SECOND / _packetSendPeriod);
|
||||||
|
@ -132,6 +132,8 @@ void DefaultCC::onACK(SequenceNumber ackNum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
setPacketSendPeriod((_packetSendPeriod * synInterval()) / (_packetSendPeriod * increase + synInterval()));
|
setPacketSendPeriod((_packetSendPeriod * synInterval()) / (_packetSendPeriod * increase + synInterval()));
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefaultCC::onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {
|
void DefaultCC::onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {
|
||||||
|
@ -218,7 +220,7 @@ void DefaultCC::stopSlowStart() {
|
||||||
// If no receiving rate is observed, we have to compute the sending
|
// If no receiving rate is observed, we have to compute the sending
|
||||||
// rate according to the current window size, and decrease it
|
// rate according to the current window size, and decrease it
|
||||||
// using the method below.
|
// using the method below.
|
||||||
setPacketSendPeriod(_congestionWindowSize / (_rtt + synInterval()));
|
setPacketSendPeriod(double(_congestionWindowSize) / (_rtt + synInterval()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,9 +40,18 @@ public:
|
||||||
void setMaxBandwidth(int maxBandwidth);
|
void setMaxBandwidth(int maxBandwidth);
|
||||||
|
|
||||||
virtual void init() {}
|
virtual void init() {}
|
||||||
virtual void onACK(SequenceNumber ackNum) {}
|
|
||||||
|
// return value specifies if connection should perform a fast re-transmit of ACK + 1 (used in TCP style congestion control)
|
||||||
|
virtual bool onACK(SequenceNumber ackNum, p_high_resolution_clock::time_point receiveTime) { return false; }
|
||||||
|
|
||||||
virtual void onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {}
|
virtual void onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) {}
|
||||||
virtual void onTimeout() {}
|
virtual void onTimeout() {}
|
||||||
|
|
||||||
|
virtual bool shouldNAK() { return true; }
|
||||||
|
virtual bool shouldACK2() { return true; }
|
||||||
|
virtual bool shouldProbe() { return true; }
|
||||||
|
|
||||||
|
virtual void onPacketSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) {}
|
||||||
protected:
|
protected:
|
||||||
void setAckInterval(int ackInterval) { _ackInterval = ackInterval; }
|
void setAckInterval(int ackInterval) { _ackInterval = ackInterval; }
|
||||||
void setRTO(int rto) { _userDefinedRTO = true; _rto = rto; }
|
void setRTO(int rto) { _userDefinedRTO = true; _rto = rto; }
|
||||||
|
@ -57,11 +66,11 @@ protected:
|
||||||
void setPacketSendPeriod(double newSendPeriod); // call this internally to ensure send period doesn't go past max bandwidth
|
void setPacketSendPeriod(double newSendPeriod); // call this internally to ensure send period doesn't go past max bandwidth
|
||||||
|
|
||||||
double _packetSendPeriod { 1.0 }; // Packet sending period, in microseconds
|
double _packetSendPeriod { 1.0 }; // Packet sending period, in microseconds
|
||||||
double _congestionWindowSize { 16.0 }; // Congestion window size, in packets
|
int _congestionWindowSize { 16 }; // Congestion window size, in packets
|
||||||
|
|
||||||
int _bandwidth { 0 }; // estimated bandwidth, packets per second
|
int _bandwidth { 0 }; // estimated bandwidth, packets per second
|
||||||
std::atomic<int> _maxBandwidth { -1 }; // Maximum desired bandwidth, bits per second
|
std::atomic<int> _maxBandwidth { -1 }; // Maximum desired bandwidth, bits per second
|
||||||
double _maxCongestionWindowSize { 0.0 }; // maximum cwnd size, in packets
|
int _maxCongestionWindowSize { 0 }; // maximum cwnd size, in packets
|
||||||
|
|
||||||
int _mss { 0 }; // Maximum Packet Size, including all packet headers
|
int _mss { 0 }; // Maximum Packet Size, including all packet headers
|
||||||
SequenceNumber _sendCurrSeqNum; // current maximum seq num sent out
|
SequenceNumber _sendCurrSeqNum; // current maximum seq num sent out
|
||||||
|
@ -102,7 +111,7 @@ public:
|
||||||
DefaultCC();
|
DefaultCC();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void onACK(SequenceNumber ackNum) override;
|
virtual bool onACK(SequenceNumber ackNum, p_high_resolution_clock::time_point receiveTime) override;
|
||||||
virtual void onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) override;
|
virtual void onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) override;
|
||||||
virtual void onTimeout() override;
|
virtual void onTimeout() override;
|
||||||
|
|
||||||
|
|
|
@ -118,12 +118,14 @@ SendQueue& Connection::getSendQueue() {
|
||||||
QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive);
|
QObject::connect(_sendQueue.get(), &SendQueue::queueInactive, this, &Connection::queueInactive);
|
||||||
QObject::connect(_sendQueue.get(), &SendQueue::timeout, this, &Connection::queueTimeout);
|
QObject::connect(_sendQueue.get(), &SendQueue::timeout, this, &Connection::queueTimeout);
|
||||||
QObject::connect(_sendQueue.get(), &SendQueue::shortCircuitLoss, this, &Connection::queueShortCircuitLoss);
|
QObject::connect(_sendQueue.get(), &SendQueue::shortCircuitLoss, this, &Connection::queueShortCircuitLoss);
|
||||||
|
|
||||||
|
|
||||||
// set defaults on the send queue from our congestion control object and estimatedTimeout()
|
// set defaults on the send queue from our congestion control object and estimatedTimeout()
|
||||||
_sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
_sendQueue->setPacketSendPeriod(_congestionControl->_packetSendPeriod);
|
||||||
_sendQueue->setSyncInterval(_synInterval);
|
_sendQueue->setSyncInterval(_synInterval);
|
||||||
_sendQueue->setEstimatedTimeout(estimatedTimeout());
|
_sendQueue->setEstimatedTimeout(estimatedTimeout());
|
||||||
_sendQueue->setFlowWindowSize(std::min(_flowWindowSize, (int) _congestionControl->_congestionWindowSize));
|
_sendQueue->setFlowWindowSize(std::min(_flowWindowSize, (int) _congestionControl->_congestionWindowSize));
|
||||||
|
_sendQueue->setProbePacketEnabled(_congestionControl->shouldProbe());
|
||||||
|
|
||||||
// give the randomized sequence number to the congestion control object
|
// give the randomized sequence number to the congestion control object
|
||||||
_congestionControl->setInitialSendSequenceNumber(_sendQueue->getCurrentSequenceNumber());
|
_congestionControl->setInitialSendSequenceNumber(_sendQueue->getCurrentSequenceNumber());
|
||||||
|
@ -150,13 +152,13 @@ void Connection::queueInactive() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::queueTimeout() {
|
void Connection::queueTimeout() {
|
||||||
updateCongestionControlAndSendQueue([this]{
|
updateCongestionControlAndSendQueue([this] {
|
||||||
_congestionControl->onTimeout();
|
_congestionControl->onTimeout();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::queueShortCircuitLoss(quint32 sequenceNumber) {
|
void Connection::queueShortCircuitLoss(quint32 sequenceNumber) {
|
||||||
updateCongestionControlAndSendQueue([this, sequenceNumber]{
|
updateCongestionControlAndSendQueue([this, sequenceNumber] {
|
||||||
_congestionControl->onLoss(SequenceNumber { sequenceNumber }, SequenceNumber { sequenceNumber });
|
_congestionControl->onLoss(SequenceNumber { sequenceNumber }, SequenceNumber { sequenceNumber });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -223,9 +225,11 @@ void Connection::sync() {
|
||||||
// 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
|
||||||
_lightACKsDuringSYN = 1;
|
_lightACKsDuringSYN = 1;
|
||||||
_acksDuringSYN = 1;
|
_acksDuringSYN = 1;
|
||||||
|
|
||||||
// we send out a periodic ACK every rate control interval
|
if (_congestionControl->_ackInterval > 1) {
|
||||||
sendACK();
|
// we send out a periodic ACK every rate control interval
|
||||||
|
sendACK();
|
||||||
|
}
|
||||||
|
|
||||||
if (_lossList.getLength() > 0) {
|
if (_lossList.getLength() > 0) {
|
||||||
// check if we need to re-transmit a loss list
|
// check if we need to re-transmit a loss list
|
||||||
|
@ -260,12 +264,17 @@ void Connection::sync() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::recordSentPackets(int dataSize, int payloadSize) {
|
void Connection::recordSentPackets(int wireSize, int payloadSize,
|
||||||
_stats.recordSentPackets(payloadSize, dataSize);
|
SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) {
|
||||||
|
_stats.recordSentPackets(payloadSize, wireSize);
|
||||||
|
|
||||||
|
_congestionControl->onPacketSent(wireSize, seqNum, timePoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::recordRetransmission() {
|
void Connection::recordRetransmission(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) {
|
||||||
_stats.record(ConnectionStats::Stats::Retransmission);
|
_stats.record(ConnectionStats::Stats::Retransmission);
|
||||||
|
|
||||||
|
_congestionControl->onPacketSent(wireSize, seqNum, timePoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::sendACK(bool wasCausedBySyncTimeout) {
|
void Connection::sendACK(bool wasCausedBySyncTimeout) {
|
||||||
|
@ -308,8 +317,8 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) {
|
||||||
|
|
||||||
// pack the available buffer size, in packets
|
// pack the available buffer size, in packets
|
||||||
// in our implementation we have no hard limit on receive buffer size, send the default value
|
// in our implementation we have no hard limit on receive buffer size, send the default value
|
||||||
_ackPacket->writePrimitive((int32_t) udt::CONNECTION_RECEIVE_BUFFER_SIZE_PACKETS);
|
_ackPacket->writePrimitive((int32_t) udt::MAX_PACKETS_IN_FLIGHT);
|
||||||
|
|
||||||
if (wasCausedBySyncTimeout) {
|
if (wasCausedBySyncTimeout) {
|
||||||
// grab the up to date packet receive speed and estimated bandwidth
|
// grab the up to date packet receive speed and estimated bandwidth
|
||||||
int32_t packetReceiveSpeed = _receiveWindow.getPacketReceiveSpeed();
|
int32_t packetReceiveSpeed = _receiveWindow.getPacketReceiveSpeed();
|
||||||
|
@ -475,23 +484,24 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in
|
||||||
} else {
|
} else {
|
||||||
_lossList.append(_lastReceivedSequenceNumber + 1, sequenceNumber - 1);
|
_lossList.append(_lastReceivedSequenceNumber + 1, sequenceNumber - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a NAK packet
|
|
||||||
sendNAK(sequenceNumber);
|
|
||||||
|
|
||||||
// figure out when we should send the next loss report, if we haven't heard anything back
|
|
||||||
_nakInterval = estimatedTimeout();
|
|
||||||
|
|
||||||
int receivedPacketsPerSecond = _receiveWindow.getPacketReceiveSpeed();
|
|
||||||
if (receivedPacketsPerSecond > 0) {
|
|
||||||
// the NAK interval is at least the _minNAKInterval
|
|
||||||
// but might be the time required for all lost packets to be retransmitted
|
|
||||||
_nakInterval += (int) (_lossList.getLength() * (USECS_PER_SECOND / receivedPacketsPerSecond));
|
|
||||||
}
|
|
||||||
|
|
||||||
// the NAK interval is at least the _minNAKInterval but might be the value calculated above, if that is larger
|
|
||||||
_nakInterval = std::max(_nakInterval, _minNAKInterval);
|
|
||||||
|
|
||||||
|
if (_congestionControl->shouldNAK()) {
|
||||||
|
// Send a NAK packet
|
||||||
|
sendNAK(sequenceNumber);
|
||||||
|
|
||||||
|
// figure out when we should send the next loss report, if we haven't heard anything back
|
||||||
|
_nakInterval = estimatedTimeout();
|
||||||
|
|
||||||
|
int receivedPacketsPerSecond = _receiveWindow.getPacketReceiveSpeed();
|
||||||
|
if (receivedPacketsPerSecond > 0) {
|
||||||
|
// the NAK interval is at least the _minNAKInterval
|
||||||
|
// but might be the time required for all lost packets to be retransmitted
|
||||||
|
_nakInterval += (int) (_lossList.getLength() * (USECS_PER_SECOND / receivedPacketsPerSecond));
|
||||||
|
}
|
||||||
|
|
||||||
|
// the NAK interval is at least the _minNAKInterval but might be the value calculated above, if that is larger
|
||||||
|
_nakInterval = std::max(_nakInterval, _minNAKInterval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wasDuplicate = false;
|
bool wasDuplicate = false;
|
||||||
|
@ -508,7 +518,10 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in
|
||||||
++_packetsSinceACK;
|
++_packetsSinceACK;
|
||||||
|
|
||||||
// check if we need to send an ACK, according to CC params
|
// check if we need to send an ACK, according to CC params
|
||||||
if (_congestionControl->_ackInterval > 0 && _packetsSinceACK >= _congestionControl->_ackInterval * _acksDuringSYN) {
|
if (_congestionControl->_ackInterval == 1) {
|
||||||
|
// using a congestion control that ACKs every packet (like TCP Vegas)
|
||||||
|
sendACK(true);
|
||||||
|
} else if (_congestionControl->_ackInterval > 0 && _packetsSinceACK >= _congestionControl->_ackInterval * _acksDuringSYN) {
|
||||||
_acksDuringSYN++;
|
_acksDuringSYN++;
|
||||||
sendACK(false);
|
sendACK(false);
|
||||||
} else if (_congestionControl->_lightACKInterval > 0
|
} else if (_congestionControl->_lightACKInterval > 0
|
||||||
|
@ -598,7 +611,8 @@ void Connection::processACK(ControlPacketPointer controlPacket) {
|
||||||
|
|
||||||
microseconds sinceLastACK2 = duration_cast<microseconds>(currentTime - lastACK2SendTime);
|
microseconds sinceLastACK2 = duration_cast<microseconds>(currentTime - lastACK2SendTime);
|
||||||
|
|
||||||
if (sinceLastACK2.count() >= _synInterval || currentACKSubSequenceNumber == _lastSentACK2) {
|
if (_congestionControl->shouldACK2()
|
||||||
|
&& (sinceLastACK2.count() >= _synInterval || currentACKSubSequenceNumber == _lastSentACK2)) {
|
||||||
// Send ACK2 packet
|
// Send ACK2 packet
|
||||||
sendACK2(currentACKSubSequenceNumber);
|
sendACK2(currentACKSubSequenceNumber);
|
||||||
|
|
||||||
|
@ -678,8 +692,11 @@ void Connection::processACK(ControlPacketPointer controlPacket) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// give this ACK to the congestion control and update the send queue parameters
|
// give this ACK to the congestion control and update the send queue parameters
|
||||||
updateCongestionControlAndSendQueue([this, ack](){
|
updateCongestionControlAndSendQueue([this, ack, &controlPacket] {
|
||||||
_congestionControl->onACK(ack);
|
if (_congestionControl->onACK(ack, controlPacket->getReceiveTime())) {
|
||||||
|
// the congestion control has told us it needs a fast re-transmit of ack + 1, add that now
|
||||||
|
_sendQueue->fastRetransmit(ack + 1);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
_stats.record(ConnectionStats::Stats::ProcessedACK);
|
_stats.record(ConnectionStats::Stats::ProcessedACK);
|
||||||
|
@ -764,7 +781,7 @@ void Connection::processNAK(ControlPacketPointer controlPacket) {
|
||||||
getSendQueue().nak(start, end);
|
getSendQueue().nak(start, end);
|
||||||
|
|
||||||
// give the loss to the congestion control object and update the send queue parameters
|
// give the loss to the congestion control object and update the send queue parameters
|
||||||
updateCongestionControlAndSendQueue([this, start, end](){
|
updateCongestionControlAndSendQueue([this, start, end] {
|
||||||
_congestionControl->onLoss(start, end);
|
_congestionControl->onLoss(start, end);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -87,8 +87,8 @@ signals:
|
||||||
void receiverHandshakeRequestComplete(const HifiSockAddr& sockAddr);
|
void receiverHandshakeRequestComplete(const HifiSockAddr& sockAddr);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void recordSentPackets(int payload, int total);
|
void recordSentPackets(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint);
|
||||||
void recordRetransmission();
|
void recordRetransmission(int wireSize, SequenceNumber sequenceNumber, p_high_resolution_clock::time_point timePoint);
|
||||||
void queueInactive();
|
void queueInactive();
|
||||||
void queueTimeout();
|
void queueTimeout();
|
||||||
void queueShortCircuitLoss(quint32 sequenceNumber);
|
void queueShortCircuitLoss(quint32 sequenceNumber);
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "SequenceNumber.h"
|
#include "SequenceNumber.h"
|
||||||
|
|
||||||
namespace udt {
|
namespace udt {
|
||||||
|
|
||||||
static const int UDP_IPV4_HEADER_SIZE = 28;
|
static const int UDP_IPV4_HEADER_SIZE = 28;
|
||||||
static const int MAX_PACKET_SIZE_WITH_UDP_HEADER = 1492;
|
static const int MAX_PACKET_SIZE_WITH_UDP_HEADER = 1492;
|
||||||
static const int MAX_PACKET_SIZE = MAX_PACKET_SIZE_WITH_UDP_HEADER - UDP_IPV4_HEADER_SIZE;
|
static const int MAX_PACKET_SIZE = MAX_PACKET_SIZE_WITH_UDP_HEADER - UDP_IPV4_HEADER_SIZE;
|
||||||
|
|
|
@ -58,8 +58,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::AssetGetInfo:
|
case PacketType::AssetGetInfo:
|
||||||
case PacketType::AssetGet:
|
case PacketType::AssetGet:
|
||||||
case PacketType::AssetUpload:
|
case PacketType::AssetUpload:
|
||||||
// Removal of extension from Asset requests
|
return static_cast<PacketVersion>(AssetServerPacketVersion::VegasCongestionControl);
|
||||||
return 18;
|
|
||||||
case PacketType::NodeIgnoreRequest:
|
case PacketType::NodeIgnoreRequest:
|
||||||
return 18; // Introduction of node ignore request (which replaced an unused packet tpye)
|
return 18; // Introduction of node ignore request (which replaced an unused packet tpye)
|
||||||
|
|
||||||
|
|
|
@ -189,6 +189,10 @@ const PacketVersion VERSION_MODEL_ENTITIES_SUPPORT_SIMPLE_HULLS = 62;
|
||||||
const PacketVersion VERSION_WEB_ENTITIES_SUPPORT_DPI = 63;
|
const PacketVersion VERSION_WEB_ENTITIES_SUPPORT_DPI = 63;
|
||||||
const PacketVersion VERSION_ENTITIES_ARROW_ACTION = 64;
|
const PacketVersion VERSION_ENTITIES_ARROW_ACTION = 64;
|
||||||
|
|
||||||
|
enum class AssetServerPacketVersion: PacketVersion {
|
||||||
|
VegasCongestionControl = 19
|
||||||
|
};
|
||||||
|
|
||||||
enum class AvatarMixerPacketVersion : PacketVersion {
|
enum class AvatarMixerPacketVersion : PacketVersion {
|
||||||
TranslationSupport = 17,
|
TranslationSupport = 17,
|
||||||
SoftAttachmentSupport,
|
SoftAttachmentSupport,
|
||||||
|
|
|
@ -180,6 +180,16 @@ void SendQueue::nak(SequenceNumber start, SequenceNumber end) {
|
||||||
_emptyCondition.notify_one();
|
_emptyCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendQueue::fastRetransmit(udt::SequenceNumber ack) {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> nakLocker(_naksLock);
|
||||||
|
_naks.insert(ack, ack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// call notify_one on the condition_variable_any in case the send thread is sleeping waiting for losses to re-send
|
||||||
|
_emptyCondition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
|
void SendQueue::overrideNAKListFromPacket(ControlPacket& packet) {
|
||||||
// this is a response from the client, re-set our timeout expiry
|
// this is a response from the client, re-set our timeout expiry
|
||||||
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
_lastReceiverResponse = QDateTime::currentMSecsSinceEpoch();
|
||||||
|
@ -242,11 +252,13 @@ bool SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket,
|
||||||
newPacket->writeSequenceNumber(sequenceNumber);
|
newPacket->writeSequenceNumber(sequenceNumber);
|
||||||
|
|
||||||
// Save packet/payload size before we move it
|
// Save packet/payload size before we move it
|
||||||
auto packetSize = newPacket->getDataSize();
|
auto packetSize = newPacket->getWireSize();
|
||||||
auto payloadSize = newPacket->getPayloadSize();
|
auto payloadSize = newPacket->getPayloadSize();
|
||||||
|
|
||||||
auto bytesWritten = sendPacket(*newPacket);
|
auto bytesWritten = sendPacket(*newPacket);
|
||||||
|
|
||||||
|
emit packetSent(packetSize, payloadSize, sequenceNumber, p_high_resolution_clock::now());
|
||||||
|
|
||||||
{
|
{
|
||||||
// Insert the packet we have just sent in the sent list
|
// Insert the packet we have just sent in the sent list
|
||||||
QWriteLocker locker(&_sentLock);
|
QWriteLocker locker(&_sentLock);
|
||||||
|
@ -256,8 +268,6 @@ bool SendQueue::sendNewPacketAndAddToSentList(std::unique_ptr<Packet> newPacket,
|
||||||
}
|
}
|
||||||
Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list");
|
Q_ASSERT_X(!newPacket, "SendQueue::sendNewPacketAndAddToSentList()", "Overriden packet in sent list");
|
||||||
|
|
||||||
emit packetSent(packetSize, payloadSize);
|
|
||||||
|
|
||||||
if (bytesWritten < 0) {
|
if (bytesWritten < 0) {
|
||||||
// this is a short-circuit loss - we failed to put this packet on the wire
|
// this is a short-circuit loss - we failed to put this packet on the wire
|
||||||
// so immediately add it to the loss list
|
// so immediately add it to the loss list
|
||||||
|
@ -328,60 +338,66 @@ void SendQueue::run() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// push the next packet timestamp forwards by the current packet send period
|
if (_packetSendPeriod > 0) {
|
||||||
auto nextPacketDelta = (newPacketCount == 2 ? 2 : 1) * _packetSendPeriod;
|
// push the next packet timestamp forwards by the current packet send period
|
||||||
nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta);
|
auto nextPacketDelta = (newPacketCount == 2 ? 2 : 1) * _packetSendPeriod;
|
||||||
|
nextPacketTimestamp += std::chrono::microseconds(nextPacketDelta);
|
||||||
|
|
||||||
// sleep as long as we need for next packet send, if we can
|
// sleep as long as we need for next packet send, if we can
|
||||||
auto now = p_high_resolution_clock::now();
|
auto now = p_high_resolution_clock::now();
|
||||||
|
|
||||||
auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - now);
|
auto timeToSleep = duration_cast<microseconds>(nextPacketTimestamp - now);
|
||||||
|
|
||||||
// we use nextPacketTimestamp so that we don't fall behind, not to force long sleeps
|
// we use nextPacketTimestamp so that we don't fall behind, not to force long sleeps
|
||||||
// we'll never allow nextPacketTimestamp to force us to sleep for more than nextPacketDelta
|
// we'll never allow nextPacketTimestamp to force us to sleep for more than nextPacketDelta
|
||||||
// so cap it to that value
|
// so cap it to that value
|
||||||
if (timeToSleep > std::chrono::microseconds(nextPacketDelta)) {
|
if (timeToSleep > std::chrono::microseconds(nextPacketDelta)) {
|
||||||
// reset the nextPacketTimestamp so that it is correct next time we come around
|
// reset the nextPacketTimestamp so that it is correct next time we come around
|
||||||
nextPacketTimestamp = now + std::chrono::microseconds(nextPacketDelta);
|
nextPacketTimestamp = now + std::chrono::microseconds(nextPacketDelta);
|
||||||
|
|
||||||
timeToSleep = std::chrono::microseconds(nextPacketDelta);
|
timeToSleep = std::chrono::microseconds(nextPacketDelta);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we're seeing SendQueues sleep for a long period of time here,
|
// we're seeing SendQueues sleep for a long period of time here,
|
||||||
// which can lock the NodeList if it's attempting to clear connections
|
// which can lock the NodeList if it's attempting to clear connections
|
||||||
// for now we guard this by capping the time this thread and sleep for
|
// for now we guard this by capping the time this thread and sleep for
|
||||||
|
|
||||||
const microseconds MAX_SEND_QUEUE_SLEEP_USECS { 2000000 };
|
const microseconds MAX_SEND_QUEUE_SLEEP_USECS { 2000000 };
|
||||||
if (timeToSleep > MAX_SEND_QUEUE_SLEEP_USECS) {
|
if (timeToSleep > MAX_SEND_QUEUE_SLEEP_USECS) {
|
||||||
qWarning() << "udt::SendQueue wanted to sleep for" << timeToSleep.count() << "microseconds";
|
qWarning() << "udt::SendQueue wanted to sleep for" << timeToSleep.count() << "microseconds";
|
||||||
qWarning() << "Capping sleep to" << MAX_SEND_QUEUE_SLEEP_USECS.count();
|
qWarning() << "Capping sleep to" << MAX_SEND_QUEUE_SLEEP_USECS.count();
|
||||||
qWarning() << "PSP:" << _packetSendPeriod << "NPD:" << nextPacketDelta
|
qWarning() << "PSP:" << _packetSendPeriod << "NPD:" << nextPacketDelta
|
||||||
<< "NPT:" << nextPacketTimestamp.time_since_epoch().count()
|
<< "NPT:" << nextPacketTimestamp.time_since_epoch().count()
|
||||||
<< "NOW:" << now.time_since_epoch().count();
|
<< "NOW:" << now.time_since_epoch().count();
|
||||||
|
|
||||||
// alright, we're in a weird state
|
// alright, we're in a weird state
|
||||||
// we want to know why this is happening so we can implement a better fix than this guard
|
// we want to know why this is happening so we can implement a better fix than this guard
|
||||||
// send some details up to the API (if the user allows us) that indicate how we could such a large timeToSleep
|
// send some details up to the API (if the user allows us) that indicate how we could such a large timeToSleep
|
||||||
static const QString SEND_QUEUE_LONG_SLEEP_ACTION = "sendqueue-sleep";
|
static const QString SEND_QUEUE_LONG_SLEEP_ACTION = "sendqueue-sleep";
|
||||||
|
|
||||||
// setup a json object with the details we want
|
// setup a json object with the details we want
|
||||||
QJsonObject longSleepObject;
|
QJsonObject longSleepObject;
|
||||||
longSleepObject["timeToSleep"] = qint64(timeToSleep.count());
|
longSleepObject["timeToSleep"] = qint64(timeToSleep.count());
|
||||||
longSleepObject["packetSendPeriod"] = _packetSendPeriod.load();
|
longSleepObject["packetSendPeriod"] = _packetSendPeriod.load();
|
||||||
longSleepObject["nextPacketDelta"] = nextPacketDelta;
|
longSleepObject["nextPacketDelta"] = nextPacketDelta;
|
||||||
longSleepObject["nextPacketTimestamp"] = qint64(nextPacketTimestamp.time_since_epoch().count());
|
longSleepObject["nextPacketTimestamp"] = qint64(nextPacketTimestamp.time_since_epoch().count());
|
||||||
longSleepObject["then"] = qint64(now.time_since_epoch().count());
|
longSleepObject["then"] = qint64(now.time_since_epoch().count());
|
||||||
|
|
||||||
// hopefully send this event using the user activity logger
|
// hopefully send this event using the user activity logger
|
||||||
UserActivityLogger::getInstance().logAction(SEND_QUEUE_LONG_SLEEP_ACTION, longSleepObject);
|
UserActivityLogger::getInstance().logAction(SEND_QUEUE_LONG_SLEEP_ACTION, longSleepObject);
|
||||||
|
|
||||||
timeToSleep = MAX_SEND_QUEUE_SLEEP_USECS;
|
timeToSleep = MAX_SEND_QUEUE_SLEEP_USECS;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(timeToSleep);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(timeToSleep);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendQueue::setProbePacketEnabled(bool enabled) {
|
||||||
|
_shouldSendProbes = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
int SendQueue::maybeSendNewPacket() {
|
int SendQueue::maybeSendNewPacket() {
|
||||||
if (!isFlowWindowFull()) {
|
if (!isFlowWindowFull()) {
|
||||||
// we didn't re-send a packet, so time to send a new one
|
// we didn't re-send a packet, so time to send a new one
|
||||||
|
@ -399,7 +415,7 @@ int SendQueue::maybeSendNewPacket() {
|
||||||
std::unique_ptr<Packet> secondPacket;
|
std::unique_ptr<Packet> secondPacket;
|
||||||
bool shouldSendPairTail = false;
|
bool shouldSendPairTail = false;
|
||||||
|
|
||||||
if (((uint32_t) nextNumber & 0xF) == 0) {
|
if (_shouldSendProbes && ((uint32_t) nextNumber & 0xF) == 0) {
|
||||||
// the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets
|
// the first packet is the first in a probe pair - every 16 (rightmost 16 bits = 0) packets
|
||||||
// pull off a second packet if we can before we unlock
|
// pull off a second packet if we can before we unlock
|
||||||
shouldSendPairTail = true;
|
shouldSendPairTail = true;
|
||||||
|
@ -492,7 +508,7 @@ bool SendQueue::maybeResendPacket() {
|
||||||
sentLocker.unlock();
|
sentLocker.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit packetRetransmitted();
|
emit packetRetransmitted(resendPacket.getWireSize(), it->first, p_high_resolution_clock::now());
|
||||||
|
|
||||||
// Signal that we did resend a packet
|
// Signal that we did resend a packet
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -64,18 +64,21 @@ public:
|
||||||
|
|
||||||
void setEstimatedTimeout(int estimatedTimeout) { _estimatedTimeout = estimatedTimeout; }
|
void setEstimatedTimeout(int estimatedTimeout) { _estimatedTimeout = estimatedTimeout; }
|
||||||
void setSyncInterval(int syncInterval) { _syncInterval = syncInterval; }
|
void setSyncInterval(int syncInterval) { _syncInterval = syncInterval; }
|
||||||
|
|
||||||
|
void setProbePacketEnabled(bool enabled);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
void ack(SequenceNumber ack);
|
void ack(SequenceNumber ack);
|
||||||
void nak(SequenceNumber start, SequenceNumber end);
|
void nak(SequenceNumber start, SequenceNumber end);
|
||||||
|
void fastRetransmit(SequenceNumber ack);
|
||||||
void overrideNAKListFromPacket(ControlPacket& packet);
|
void overrideNAKListFromPacket(ControlPacket& packet);
|
||||||
void handshakeACK(SequenceNumber initialSequenceNumber);
|
void handshakeACK(SequenceNumber initialSequenceNumber);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void packetSent(int dataSize, int payloadSize);
|
void packetSent(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint);
|
||||||
void packetRetransmitted();
|
void packetRetransmitted(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint);
|
||||||
|
|
||||||
void queueInactive();
|
void queueInactive();
|
||||||
|
|
||||||
|
@ -139,6 +142,9 @@ private:
|
||||||
std::condition_variable _handshakeACKCondition;
|
std::condition_variable _handshakeACKCondition;
|
||||||
|
|
||||||
std::condition_variable_any _emptyCondition;
|
std::condition_variable_any _emptyCondition;
|
||||||
|
|
||||||
|
|
||||||
|
std::atomic<bool> _shouldSendProbes { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,14 @@
|
||||||
|
|
||||||
#include "SequenceNumber.h"
|
#include "SequenceNumber.h"
|
||||||
|
|
||||||
|
#include <QtCore/QMetaType>
|
||||||
|
|
||||||
|
using namespace udt;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(SequenceNumber);
|
||||||
|
|
||||||
|
static const int sequenceNumberMetaTypeID = qRegisterMetaType<SequenceNumber>();
|
||||||
|
|
||||||
int udt::seqlen(const SequenceNumber& seq1, const SequenceNumber& seq2) {
|
int udt::seqlen(const SequenceNumber& seq1, const SequenceNumber& seq2) {
|
||||||
return (seq1._value <= seq2._value) ? (seq2._value - seq1._value + 1)
|
return (seq1._value <= seq2._value) ? (seq2._value - seq1._value + 1)
|
||||||
: (seq2._value - seq1._value + SequenceNumber::MAX + 2);
|
: (seq2._value - seq1._value + SequenceNumber::MAX + 2);
|
||||||
|
|
|
@ -289,6 +289,9 @@ void Socket::messageFailed(Connection* connection, Packet::MessageNumber message
|
||||||
void Socket::readPendingDatagrams() {
|
void Socket::readPendingDatagrams() {
|
||||||
int packetSizeWithHeader = -1;
|
int packetSizeWithHeader = -1;
|
||||||
while ((packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) {
|
while ((packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) {
|
||||||
|
// grab a time point we can mark as the receive time of this packet
|
||||||
|
auto receiveTime = p_high_resolution_clock::now();
|
||||||
|
|
||||||
// setup a HifiSockAddr to read into
|
// setup a HifiSockAddr to read into
|
||||||
HifiSockAddr senderSockAddr;
|
HifiSockAddr senderSockAddr;
|
||||||
|
|
||||||
|
@ -311,6 +314,7 @@ void Socket::readPendingDatagrams() {
|
||||||
// we have a registered unfiltered handler for this HifiSockAddr - call that and return
|
// we have a registered unfiltered handler for this HifiSockAddr - call that and return
|
||||||
if (it->second) {
|
if (it->second) {
|
||||||
auto basePacket = BasePacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
auto basePacket = BasePacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
||||||
|
basePacket->setReceiveTime(receiveTime);
|
||||||
it->second(std::move(basePacket));
|
it->second(std::move(basePacket));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,6 +327,7 @@ void Socket::readPendingDatagrams() {
|
||||||
if (isControlPacket) {
|
if (isControlPacket) {
|
||||||
// setup a control packet from the data we just read
|
// setup a control packet from the data we just read
|
||||||
auto controlPacket = ControlPacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
auto controlPacket = ControlPacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
||||||
|
controlPacket->setReceiveTime(receiveTime);
|
||||||
|
|
||||||
// move this control packet to the matching connection, if there is one
|
// move this control packet to the matching connection, if there is one
|
||||||
auto connection = findOrCreateConnection(senderSockAddr);
|
auto connection = findOrCreateConnection(senderSockAddr);
|
||||||
|
@ -334,6 +339,7 @@ void Socket::readPendingDatagrams() {
|
||||||
} else {
|
} else {
|
||||||
// setup a Packet from the data we just read
|
// setup a Packet from the data we just read
|
||||||
auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
auto packet = Packet::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
|
||||||
|
packet->setReceiveTime(receiveTime);
|
||||||
|
|
||||||
// call our verification operator to see if this packet is verified
|
// call our verification operator to see if this packet is verified
|
||||||
if (!_packetFilterOperator || _packetFilterOperator(*packet)) {
|
if (!_packetFilterOperator || _packetFilterOperator(*packet)) {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
#include <QtNetwork/QUdpSocket>
|
#include <QtNetwork/QUdpSocket>
|
||||||
|
|
||||||
#include "../HifiSockAddr.h"
|
#include "../HifiSockAddr.h"
|
||||||
#include "CongestionControl.h"
|
#include "TCPVegasCC.h"
|
||||||
#include "Connection.h"
|
#include "Connection.h"
|
||||||
|
|
||||||
//#define UDT_CONNECTION_DEBUG
|
//#define UDT_CONNECTION_DEBUG
|
||||||
|
@ -130,8 +130,8 @@ private:
|
||||||
QTimer* _synTimer { nullptr };
|
QTimer* _synTimer { nullptr };
|
||||||
|
|
||||||
int _maxBandwidth { -1 };
|
int _maxBandwidth { -1 };
|
||||||
|
|
||||||
std::unique_ptr<CongestionControlVirtualFactory> _ccFactory { new CongestionControlFactory<DefaultCC>() };
|
std::unique_ptr<CongestionControlVirtualFactory> _ccFactory { new CongestionControlFactory<TCPVegasCC>() };
|
||||||
|
|
||||||
friend UDTTest;
|
friend UDTTest;
|
||||||
};
|
};
|
||||||
|
|
282
libraries/networking/src/udt/TCPVegasCC.cpp
Normal file
282
libraries/networking/src/udt/TCPVegasCC.cpp
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
//
|
||||||
|
// TCPVegasCC.cpp
|
||||||
|
// libraries/networking/src/udt
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2016-09-20.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "TCPVegasCC.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QtGlobal>
|
||||||
|
|
||||||
|
using namespace udt;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
TCPVegasCC::TCPVegasCC() {
|
||||||
|
_packetSendPeriod = 0.0;
|
||||||
|
_congestionWindowSize = 2;
|
||||||
|
|
||||||
|
setAckInterval(1); // TCP sends an ACK for every packet received
|
||||||
|
|
||||||
|
// set our minimum RTT variables to the maximum possible value
|
||||||
|
// we can't do this as a member initializer until our VS has support for constexpr
|
||||||
|
_currentMinRTT = std::numeric_limits<int>::max();
|
||||||
|
_baseRTT = std::numeric_limits<int>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TCPVegasCC::onACK(SequenceNumber ack, p_high_resolution_clock::time_point receiveTime) {
|
||||||
|
auto it = _sentPacketTimes.find(ack);
|
||||||
|
|
||||||
|
auto previousAck = _lastACK;
|
||||||
|
_lastACK = ack;
|
||||||
|
|
||||||
|
if (it != _sentPacketTimes.end()) {
|
||||||
|
|
||||||
|
// calculate the RTT (receive time - time ACK sent)
|
||||||
|
int lastRTT = duration_cast<microseconds>(receiveTime - it->second).count();
|
||||||
|
|
||||||
|
const int MAX_RTT_SAMPLE_MICROSECONDS = 10000000;
|
||||||
|
|
||||||
|
if (lastRTT < 0) {
|
||||||
|
Q_ASSERT_X(false, __FUNCTION__, "calculated an RTT that is not > 0");
|
||||||
|
return false;
|
||||||
|
} else if (lastRTT == 0) {
|
||||||
|
// we do not allow a zero microsecond RTT (as per the UNIX kernel implementation of TCP Vegas)
|
||||||
|
lastRTT = 1;
|
||||||
|
} else if (lastRTT > MAX_RTT_SAMPLE_MICROSECONDS) {
|
||||||
|
// we cap the lastRTT to MAX_RTT_SAMPLE_MICROSECONDS to avoid overflows in window size calculations
|
||||||
|
lastRTT = MAX_RTT_SAMPLE_MICROSECONDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_ewmaRTT == -1) {
|
||||||
|
// first RTT sample - set _ewmaRTT to the value and set the variance to half the value
|
||||||
|
_ewmaRTT = lastRTT;
|
||||||
|
_rttVariance = lastRTT / 2;
|
||||||
|
} else {
|
||||||
|
// This updates the RTT using exponential weighted moving average
|
||||||
|
// This is the Jacobson's forumla for RTT estimation
|
||||||
|
// http://www.mathcs.emory.edu/~cheung/Courses/455/Syllabus/7-transport/Jacobson-88.pdf
|
||||||
|
|
||||||
|
// Estimated RTT = (1 - x)(estimatedRTT) + (x)(sampleRTT)
|
||||||
|
// (where x = 0.125 via Jacobson)
|
||||||
|
|
||||||
|
// Deviation = (1 - x)(deviation) + x |sampleRTT - estimatedRTT|
|
||||||
|
// (where x = 0.25 via Jacobson)
|
||||||
|
|
||||||
|
static const int RTT_ESTIMATION_ALPHA = 8;
|
||||||
|
static const int RTT_ESTIMATION_VARIANCE_ALPHA = 4;
|
||||||
|
|
||||||
|
_ewmaRTT = (_ewmaRTT * (RTT_ESTIMATION_ALPHA - 1) + lastRTT) / RTT_ESTIMATION_ALPHA;
|
||||||
|
_rttVariance = (_rttVariance * (RTT_ESTIMATION_VARIANCE_ALPHA- 1)
|
||||||
|
+ abs(lastRTT - _ewmaRTT)) / RTT_ESTIMATION_VARIANCE_ALPHA;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add 1 to the number of ACKs during this RTT
|
||||||
|
++_numACKs;
|
||||||
|
|
||||||
|
// keep track of the lowest RTT during connection
|
||||||
|
_baseRTT = std::min(_baseRTT, lastRTT);
|
||||||
|
|
||||||
|
// find the min RTT during the last RTT
|
||||||
|
_currentMinRTT = std::min(_currentMinRTT, lastRTT);
|
||||||
|
|
||||||
|
auto sinceLastAdjustment = duration_cast<microseconds>(p_high_resolution_clock::now() - _lastAdjustmentTime).count();
|
||||||
|
if (sinceLastAdjustment >= _ewmaRTT) {
|
||||||
|
performCongestionAvoidance(ack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove this sent packet time from the hash
|
||||||
|
_sentPacketTimes.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
++_numACKSinceFastRetransmit;
|
||||||
|
|
||||||
|
// perform the fast re-transmit check if this is a duplicate ACK or if this is the first or second ACK
|
||||||
|
// after a previous fast re-transmit
|
||||||
|
if (ack == previousAck || _numACKSinceFastRetransmit < 3) {
|
||||||
|
// we may need to re-send ackNum + 1 if it has been more than our estimated timeout since it was sent
|
||||||
|
|
||||||
|
auto it = _sentPacketTimes.find(ack + 1);
|
||||||
|
if (it != _sentPacketTimes.end()) {
|
||||||
|
auto estimatedTimeout = _ewmaRTT + _rttVariance * 4;
|
||||||
|
|
||||||
|
auto now = p_high_resolution_clock::now();
|
||||||
|
auto sinceSend = duration_cast<microseconds>(now - it->second).count();
|
||||||
|
|
||||||
|
if (sinceSend >= estimatedTimeout) {
|
||||||
|
// break out of slow start, we've decided this is loss
|
||||||
|
_slowStart = false;
|
||||||
|
|
||||||
|
// reset the fast re-transmit counter
|
||||||
|
_numACKSinceFastRetransmit = 0;
|
||||||
|
|
||||||
|
// return true so the caller knows we needed a fast re-transmit
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is the 3rd duplicate ACK, we fallback to Reno's fast re-transmit
|
||||||
|
static const int RENO_FAST_RETRANSMIT_DUPLICATE_COUNT = 3;
|
||||||
|
|
||||||
|
++_duplicateACKCount;
|
||||||
|
|
||||||
|
if (ack == previousAck && _duplicateACKCount == RENO_FAST_RETRANSMIT_DUPLICATE_COUNT) {
|
||||||
|
// break out of slow start, we just hit loss
|
||||||
|
_slowStart = false;
|
||||||
|
|
||||||
|
// reset our fast re-transmit counters
|
||||||
|
_numACKSinceFastRetransmit = 0;
|
||||||
|
_duplicateACKCount = 0;
|
||||||
|
|
||||||
|
// return true so the caller knows we needed a fast re-transmit
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_duplicateACKCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACK processed, no fast re-transmit required
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPVegasCC::performCongestionAvoidance(udt::SequenceNumber ack) {
|
||||||
|
static int VEGAS_ALPHA_SEGMENTS = 4;
|
||||||
|
static int VEGAS_BETA_SEGMENTS = 6;
|
||||||
|
static int VEGAS_GAMMA_SEGMENTS = 1;
|
||||||
|
|
||||||
|
// http://pages.cs.wisc.edu/~akella/CS740/S08/740-Papers/BOP94.pdf
|
||||||
|
// Use the Vegas algorithm to see if we should
|
||||||
|
// increase or decrease the congestion window size, and by how much
|
||||||
|
|
||||||
|
// Grab the minimum RTT seen during the last RTT (since the last performed congestion avoidance)
|
||||||
|
|
||||||
|
// Taking the min avoids the effects of delayed ACKs
|
||||||
|
// (though congestion may be noticed a bit later)
|
||||||
|
int rtt = _currentMinRTT;
|
||||||
|
|
||||||
|
int64_t windowSizeDiff = (int64_t) _congestionWindowSize * (rtt - _baseRTT) / _baseRTT;
|
||||||
|
|
||||||
|
if (_numACKs <= 2) {
|
||||||
|
performRenoCongestionAvoidance(ack);
|
||||||
|
} else {
|
||||||
|
if (_slowStart) {
|
||||||
|
if (windowSizeDiff > VEGAS_GAMMA_SEGMENTS) {
|
||||||
|
// we're going too fast - this breaks us out of slow start and we switch to linear increase/decrease
|
||||||
|
_slowStart = false;
|
||||||
|
|
||||||
|
int expectedWindowSize = _congestionWindowSize * _baseRTT / rtt;
|
||||||
|
_baseRTT = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
|
// drop the congestion window size to the expected size, if smaller
|
||||||
|
_congestionWindowSize = std::min(_congestionWindowSize, expectedWindowSize + 1);
|
||||||
|
|
||||||
|
} else if (++_slowStartOddAdjust & 1) {
|
||||||
|
// we're in slow start and not going too fast
|
||||||
|
// this means that once every second RTT we perform exponential congestion window growth
|
||||||
|
_congestionWindowSize *= 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this is the normal linear increase/decrease of the Vegas algorithm
|
||||||
|
// to figure out where the congestion window should be
|
||||||
|
if (windowSizeDiff > VEGAS_BETA_SEGMENTS) {
|
||||||
|
// the old congestion window was too fast (difference > beta)
|
||||||
|
// so reduce it to slow down
|
||||||
|
--_congestionWindowSize;
|
||||||
|
|
||||||
|
} else if (windowSizeDiff < VEGAS_ALPHA_SEGMENTS) {
|
||||||
|
// there aren't enough packets on the wire, add more to the congestion window
|
||||||
|
++_congestionWindowSize;
|
||||||
|
} else {
|
||||||
|
// sending rate seems good, no congestion window adjustment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we never allow the congestion window to be smaller than two packets
|
||||||
|
static int VEGAS_CW_MIN_PACKETS = 2;
|
||||||
|
if (_congestionWindowSize < VEGAS_CW_MIN_PACKETS) {
|
||||||
|
_congestionWindowSize = VEGAS_CW_MIN_PACKETS;
|
||||||
|
} else if (_congestionWindowSize > udt::MAX_PACKETS_IN_FLIGHT) {
|
||||||
|
_congestionWindowSize = udt::MAX_PACKETS_IN_FLIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark this as the last adjustment time
|
||||||
|
_lastAdjustmentTime = p_high_resolution_clock::now();
|
||||||
|
|
||||||
|
// reset our state for the next RTT
|
||||||
|
_currentMinRTT = std::numeric_limits<int>::max();
|
||||||
|
|
||||||
|
// reset our count of collected RTT samples
|
||||||
|
_numACKs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TCPVegasCC::isCongestionWindowLimited() {
|
||||||
|
if (_slowStart) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return seqlen(_sendCurrSeqNum, _lastACK) < _congestionWindowSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPVegasCC::performRenoCongestionAvoidance(SequenceNumber ack) {
|
||||||
|
if (!isCongestionWindowLimited()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int numAcked = _numACKs;
|
||||||
|
|
||||||
|
if (_slowStart) {
|
||||||
|
// while in slow start we grow the congestion window by the number of ACKed packets
|
||||||
|
// allowing it to grow as high as the slow start threshold
|
||||||
|
int congestionWindow = _congestionWindowSize + numAcked;
|
||||||
|
|
||||||
|
if (congestionWindow > udt::MAX_PACKETS_IN_FLIGHT) {
|
||||||
|
// we're done with slow start, set the congestion window to the slow start threshold
|
||||||
|
_congestionWindowSize = udt::MAX_PACKETS_IN_FLIGHT;
|
||||||
|
|
||||||
|
// figure out how many left over ACKs we should apply using the regular reno congestion avoidance
|
||||||
|
numAcked = congestionWindow - udt::MAX_PACKETS_IN_FLIGHT;
|
||||||
|
} else {
|
||||||
|
_congestionWindowSize = congestionWindow;
|
||||||
|
numAcked = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab the size of the window prior to reno additive increase
|
||||||
|
int preAIWindowSize = _congestionWindowSize;
|
||||||
|
|
||||||
|
if (numAcked > 0) {
|
||||||
|
// Once we are out of slow start, we use additive increase to grow the window slowly.
|
||||||
|
// We grow the congestion window by a single packet everytime the entire congestion window is sent.
|
||||||
|
|
||||||
|
// If credits accumulated at a higher preAIWindowSize, apply them gently now.
|
||||||
|
if (_ackAICount >= preAIWindowSize) {
|
||||||
|
_ackAICount = 0;
|
||||||
|
++_congestionWindowSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// increase the window size by (1 / window size) for every ACK received
|
||||||
|
_ackAICount += numAcked;
|
||||||
|
if (_ackAICount >= preAIWindowSize) {
|
||||||
|
// when _ackAICount % preAIWindowSize == 0 then _ackAICount is 0
|
||||||
|
// when _ackAICount % preAIWindowSize != 0 then _ackAICount is _ackAICount - (_ackAICount % preAIWindowSize)
|
||||||
|
|
||||||
|
int delta = _ackAICount / preAIWindowSize;
|
||||||
|
|
||||||
|
_ackAICount -= delta * preAIWindowSize;
|
||||||
|
_congestionWindowSize += delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TCPVegasCC::onPacketSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) {
|
||||||
|
if (_sentPacketTimes.find(seqNum) == _sentPacketTimes.end()) {
|
||||||
|
_sentPacketTimes[seqNum] = timePoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
77
libraries/networking/src/udt/TCPVegasCC.h
Normal file
77
libraries/networking/src/udt/TCPVegasCC.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
//
|
||||||
|
// TCPVegasCC.h
|
||||||
|
// libraries/networking/src/udt
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2016-09-20.
|
||||||
|
// Copyright 2016 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef hifi_TCPVegasCC_h
|
||||||
|
#define hifi_TCPVegasCC_h
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "CongestionControl.h"
|
||||||
|
#include "Constants.h"
|
||||||
|
|
||||||
|
namespace udt {
|
||||||
|
|
||||||
|
|
||||||
|
class TCPVegasCC : public CongestionControl {
|
||||||
|
public:
|
||||||
|
TCPVegasCC();
|
||||||
|
|
||||||
|
virtual bool onACK(SequenceNumber ackNum, p_high_resolution_clock::time_point receiveTime) override;
|
||||||
|
virtual void onLoss(SequenceNumber rangeStart, SequenceNumber rangeEnd) override {};
|
||||||
|
virtual void onTimeout() override {};
|
||||||
|
|
||||||
|
virtual bool shouldNAK() override { return false; }
|
||||||
|
virtual bool shouldACK2() override { return false; }
|
||||||
|
virtual bool shouldProbe() override { return false; }
|
||||||
|
|
||||||
|
virtual void onPacketSent(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void performCongestionAvoidance(SequenceNumber ack);
|
||||||
|
virtual void setInitialSendSequenceNumber(SequenceNumber seqNum) override { _lastACK = seqNum - 1; }
|
||||||
|
private:
|
||||||
|
bool isCongestionWindowLimited();
|
||||||
|
void performRenoCongestionAvoidance(SequenceNumber ack);
|
||||||
|
|
||||||
|
using PacketTimeList = std::map<SequenceNumber, p_high_resolution_clock::time_point>;
|
||||||
|
PacketTimeList _sentPacketTimes; // Map of sequence numbers to sent time
|
||||||
|
|
||||||
|
p_high_resolution_clock::time_point _lastAdjustmentTime; // Time of last congestion control adjustment
|
||||||
|
|
||||||
|
bool _slowStart { true }; // Marker for slow start phase
|
||||||
|
|
||||||
|
SequenceNumber _lastACK; // Sequence number of last packet that was ACKed
|
||||||
|
|
||||||
|
int _numACKSinceFastRetransmit { 3 }; // Number of ACKs received since fast re-transmit, default avoids immediate re-transmit
|
||||||
|
|
||||||
|
int _currentMinRTT; // Current min RTT during last RTT (since last congestion avoidance check), in microseconds
|
||||||
|
int _baseRTT; // Lowest RTT during connection, in microseconds
|
||||||
|
int _ewmaRTT { -1 }; // Exponential weighted moving average RTT
|
||||||
|
int _rttVariance { 0 }; // Variance in collected RTT values
|
||||||
|
|
||||||
|
int _numACKs { 0 }; // Number of ACKs received during the last RTT (since last performed congestion avoidance)
|
||||||
|
|
||||||
|
int _ackAICount { 0 }; // Counter for number of ACKs received for Reno additive increase
|
||||||
|
int _duplicateACKCount { 0 }; // Counter for duplicate ACKs received
|
||||||
|
|
||||||
|
int _slowStartOddAdjust { 0 }; // Marker for every window adjustment every other RTT in slow-start
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // hifi_TCPVegasCC_h
|
|
@ -38,6 +38,12 @@ void BatchLoader::start() {
|
||||||
|
|
||||||
_started = true;
|
_started = true;
|
||||||
|
|
||||||
|
if (_urls.size() == 0) {
|
||||||
|
_finished = true;
|
||||||
|
emit finished(_data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& rawURL : _urls) {
|
for (const auto& rawURL : _urls) {
|
||||||
QUrl url = expandScriptUrl(normalizeScriptURL(rawURL));
|
QUrl url = expandScriptUrl(normalizeScriptURL(rawURL));
|
||||||
|
|
||||||
|
|
|
@ -1199,6 +1199,11 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there are no URLs left to download, don't bother attempting to download anything and return early
|
||||||
|
if (urls.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BatchLoader* loader = new BatchLoader(urls);
|
BatchLoader* loader = new BatchLoader(urls);
|
||||||
EntityItemID capturedEntityIdentifier = currentEntityIdentifier;
|
EntityItemID capturedEntityIdentifier = currentEntityIdentifier;
|
||||||
QUrl capturedSandboxURL = currentSandboxURL;
|
QUrl capturedSandboxURL = currentSandboxURL;
|
||||||
|
|
|
@ -143,13 +143,13 @@ void XMLHttpRequestClass::open(const QString& method, const QString& url, bool a
|
||||||
if (url.toLower().left(METAVERSE_API_URL.length()) == METAVERSE_API_URL) {
|
if (url.toLower().left(METAVERSE_API_URL.length()) == METAVERSE_API_URL) {
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
|
|
||||||
if (accountManager->hasValidAccessToken()) {
|
if (_url.scheme() == "https" && accountManager->hasValidAccessToken()) {
|
||||||
QUrlQuery urlQuery(_url.query());
|
static const QString HTTP_AUTHORIZATION_HEADER = "Authorization";
|
||||||
urlQuery.addQueryItem("access_token", accountManager->getAccountInfo().getAccessToken().token);
|
QString bearerString = "Bearer " + accountManager->getAccountInfo().getAccessToken().token;
|
||||||
_url.setQuery(urlQuery);
|
_request.setRawHeader(HTTP_AUTHORIZATION_HEADER.toLocal8Bit(), bearerString.toLocal8Bit());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!username.isEmpty()) {
|
if (!username.isEmpty()) {
|
||||||
_url.setUserName(username);
|
_url.setUserName(username);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
#include <QtCore/QMetaType>
|
||||||
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
@ -47,5 +49,8 @@ using p_high_resolution_clock = std::chrono::high_resolution_clock;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(p_high_resolution_clock::time_point);
|
||||||
|
|
||||||
|
static const int timePointMetaTypeID = qRegisterMetaType<p_high_resolution_clock::time_point>();
|
||||||
|
|
||||||
#endif // hifi_PortableHighResolutionClock_h
|
#endif // hifi_PortableHighResolutionClock_h
|
||||||
|
|
|
@ -86,13 +86,10 @@ QString QmlWebWindowClass::getURL() const {
|
||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK find a good place to declare and store this
|
|
||||||
extern QString fixupHifiUrl(const QString& urlString);
|
|
||||||
|
|
||||||
void QmlWebWindowClass::setURL(const QString& urlString) {
|
void QmlWebWindowClass::setURL(const QString& urlString) {
|
||||||
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
DependencyManager::get<OffscreenUi>()->executeOnUiThread([=] {
|
||||||
if (!_qmlWindow.isNull()) {
|
if (!_qmlWindow.isNull()) {
|
||||||
_qmlWindow->setProperty(URL_PROPERTY, fixupHifiUrl(urlString));
|
_qmlWindow->setProperty(URL_PROPERTY, urlString);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class QmlWebWindowClass : public QmlWindowClass {
|
||||||
public:
|
public:
|
||||||
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QString getURL() const;
|
QString getURL() const;
|
||||||
void setURL(const QString& url);
|
void setURL(const QString& url);
|
||||||
|
|
||||||
|
|
|
@ -2091,12 +2091,12 @@ function MyController(hand) {
|
||||||
var TEAR_AWAY_DISTANCE = 0.1;
|
var TEAR_AWAY_DISTANCE = 0.1;
|
||||||
var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props);
|
var dist = distanceBetweenPointAndEntityBoundingBox(handPosition, props);
|
||||||
if (dist > TEAR_AWAY_DISTANCE) {
|
if (dist > TEAR_AWAY_DISTANCE) {
|
||||||
this.autoUnequipCounter += 1;
|
this.autoUnequipCounter += deltaTime;
|
||||||
} else {
|
} else {
|
||||||
this.autoUnequipCounter = 0;
|
this.autoUnequipCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.autoUnequipCounter > 1) {
|
if (this.autoUnequipCounter > 0.25) {
|
||||||
// for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip.
|
// for whatever reason, the held/equipped entity has been pulled away. ungrab or unequip.
|
||||||
print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." +
|
print("handControllerGrab -- autoreleasing held or equipped item because it is far from hand." +
|
||||||
props.parentID + ", dist = " + dist);
|
props.parentID + ", dist = " + dist);
|
||||||
|
|
|
@ -18,10 +18,12 @@
|
||||||
#include <QtCore/QElapsedTimer>
|
#include <QtCore/QElapsedTimer>
|
||||||
#include <QtCore/QLoggingCategory>
|
#include <QtCore/QLoggingCategory>
|
||||||
#include <QtCore/QRegularExpression>
|
#include <QtCore/QRegularExpression>
|
||||||
|
#include <QtCore/QSettings>
|
||||||
#include <QtCore/QTimer>
|
#include <QtCore/QTimer>
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
#include <QtCore/QThreadPool>
|
#include <QtCore/QThreadPool>
|
||||||
|
|
||||||
|
|
||||||
#include <QtGui/QGuiApplication>
|
#include <QtGui/QGuiApplication>
|
||||||
#include <QtGui/QResizeEvent>
|
#include <QtGui/QResizeEvent>
|
||||||
#include <QtGui/QWindow>
|
#include <QtGui/QWindow>
|
||||||
|
|
|
@ -25,7 +25,7 @@ const QCommandLineOption TARGET_OPTION {
|
||||||
"IP:PORT or HOSTNAME:PORT"
|
"IP:PORT or HOSTNAME:PORT"
|
||||||
};
|
};
|
||||||
const QCommandLineOption PACKET_SIZE {
|
const QCommandLineOption PACKET_SIZE {
|
||||||
"packet-size", "size for sent packets in bytes (defaults to " + QString(MAX_PACKET_SIZE) + ")", "bytes",
|
"packet-size", "size for sent packets in bytes (defaults to " + QString::number(udt::MAX_PACKET_SIZE) + ")", "bytes",
|
||||||
QString(udt::MAX_PACKET_SIZE)
|
QString(udt::MAX_PACKET_SIZE)
|
||||||
};
|
};
|
||||||
const QCommandLineOption MIN_PACKET_SIZE {
|
const QCommandLineOption MIN_PACKET_SIZE {
|
||||||
|
|
Loading…
Reference in a new issue