diff --git a/config.default.yml b/config.default.yml index bbfd85e..9adf047 100644 --- a/config.default.yml +++ b/config.default.yml @@ -4,7 +4,7 @@ backend: # TODO webdav: false performance: - # Whiteboard information broadcasting frequency (in /s) + # Whiteboard information broadcasting frequency (in Hz i.e. /s) # => diminishing this will result in more latency whiteboardInfoBroadcastFreq: 1 @@ -15,7 +15,8 @@ frontend: # Show smallest screen indicator showSmallestScreenIndicator: true performance: - pointerEventsThreshold: - minDistDelta: 1 - minTimeDelta: 33 + pointerEventsThrottling: + - fromNbUser: 0 + minDistDelta: 1 + maxFreq: 30 refreshInfoFreq: 5 diff --git a/scripts/config-schema.json b/scripts/config-schema.json index a389e1e..eeb1a80 100644 --- a/scripts/config-schema.json +++ b/scripts/config-schema.json @@ -41,20 +41,28 @@ "performance": { "type": "object", "additionalProperties": false, - "required": ["pointerEventsThreshold", "refreshInfoFreq"], + "required": ["pointerEventsThrottling", "refreshInfoFreq"], "properties": { - "pointerEventsThreshold": { - "type": "object", - "additionalProperties": false, - "required": ["minDistDelta", "minTimeDelta"], - "properties": { - "minDistDelta": { - "type": "number", - "minimum": 0 - }, - "minTimeDelta": { - "type": "number", - "minimum": 0 + "pointerEventsThrottling": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "additionalProperties": false, + "required": ["fromNbUser", "minDistDelta", "maxFreq"], + "properties": { + "fromNbUser": { + "type": "number", + "minimum": 0 + }, + "minDistDelta": { + "type": "number", + "minimum": 0 + }, + "maxFreq": { + "type": "number", + "minimum": 0 + } } } }, diff --git a/scripts/utils.js b/scripts/utils.js index 1c12423..754a3f2 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -44,11 +44,28 @@ function getConfig(path) { */ function isConfigValid(config, warn = true) { const validate = ajv.compile(configSchema); - const isValid = validate(config); + const isValidAgainstSchema = validate(config); - if (!isValid && warn) console.warn(validate.errors); + if (!isValidAgainstSchema && warn) console.warn(validate.errors); - return isValid; + let structureIsValid = false; + try { + structureIsValid = config.frontend.performance.pointerEventsThrottling.some( + (item) => item.fromNbUser === 0 + ); + } catch (e) { + if (!e instanceof TypeError) { + throw e; + } + } + + if (!structureIsValid && warn) + console.warn( + "At least one item under frontend.performance.pointerEventsThrottling" + + "must have fromNbUser set to 0" + ); + + return isValidAgainstSchema && structureIsValid; } /** diff --git a/src/js/services/ConfigService.js b/src/js/services/ConfigService.js index 6f8ef64..74f3967 100644 --- a/src/js/services/ConfigService.js +++ b/src/js/services/ConfigService.js @@ -1,4 +1,12 @@ +import { getThrottling } from "./ConfigService.utils"; + class ConfigService { + /** + * @type {object} + * @private + */ + _configFromServer = {}; + /** * @readonly * @type {boolean} @@ -13,15 +21,9 @@ class ConfigService { /** * @readonly - * @type {number} + * @type {{minDistDelta: number, minTimeDelta: number}} */ - pointerEventsThresholdMinDistDelta = 0; - - /** - * @readonly - * @type {number} - */ - pointerEventsThresholdMinTimeDelta = 0; + pointerEventsThrottling = { minDistDelta: 0, minTimeDelta: 0 }; /** * @readonly @@ -32,19 +34,31 @@ class ConfigService { /** * Init the service from the config sent by the server * - * @param {object} serverResponse + * @param {object} configFromServer */ - initFromServer(serverResponse) { - const { common } = serverResponse; + initFromServer(configFromServer) { + this._configFromServer = configFromServer; + + const { common } = configFromServer; const { readOnlyOnWhiteboardLoad, showSmallestScreenIndicator, performance } = common; this.readOnlyOnWhiteboardLoad = readOnlyOnWhiteboardLoad; this.showSmallestScreenIndicator = showSmallestScreenIndicator; - this.pointerEventsThresholdMinDistDelta = performance.pointerEventsThreshold.minDistDelta; - this.pointerEventsThresholdMinTimeDelta = performance.pointerEventsThreshold.minTimeDelta; this.refreshInfoInterval = 1000 / performance.refreshInfoFreq; - console.log("Whiteboard config from server:", serverResponse, "parsed:", this); + console.log("Whiteboard config from server:", configFromServer, "parsed:", this); + } + + /** + * TODO + */ + refreshNbUserDependant(nbUser) { + const { _configFromServer } = this; + const { common } = _configFromServer; + const { performance } = common; + const { pointerEventsThrottling } = performance; + + this.pointerEventsThrottling = getThrottling(pointerEventsThrottling, nbUser); } } diff --git a/src/js/services/ConfigService.utils.js b/src/js/services/ConfigService.utils.js new file mode 100644 index 0000000..f2111ac --- /dev/null +++ b/src/js/services/ConfigService.utils.js @@ -0,0 +1,21 @@ +/** + * Helper to extract the correct correct throttling values based on the config and the number of user + * + * @param {Array.<{fromNbUser: number, minDistDelta: number, maxFreq: number}>} pointerEventsThrottling + * @param {number} nbUser + * @return {{minDistDelta: number, minTimeDelta: number}} + */ +export function getThrottling(pointerEventsThrottling, nbUser) { + let tmpOut = pointerEventsThrottling[0]; + let lastDistToNbUser = nbUser - tmpOut.fromNbUser; + if (lastDistToNbUser < 0) lastDistToNbUser = Number.MAX_VALUE; + for (const el of pointerEventsThrottling) { + const distToNbUser = nbUser - el.fromNbUser; + if (el.fromNbUser <= nbUser && distToNbUser <= lastDistToNbUser) { + tmpOut = el; + lastDistToNbUser = distToNbUser; + } + } + + return { minDistDelta: tmpOut.minDistDelta, minTimeDelta: 1000 * (1 / tmpOut.maxFreq) }; +} diff --git a/src/js/services/ConfigService.utils.test.js b/src/js/services/ConfigService.utils.test.js new file mode 100644 index 0000000..d60ed41 --- /dev/null +++ b/src/js/services/ConfigService.utils.test.js @@ -0,0 +1,29 @@ +import { getThrottling } from "./ConfigService.utils"; + +test("Simple throttling config", () => { + const throttling = [{ fromNbUser: 0, minDistDelta: 1, maxFreq: 1 }]; + + const target0 = { minDistDelta: 1, minTimeDelta: 1000 }; + expect(getThrottling(throttling, 0)).toEqual(target0); + + const target100 = { minDistDelta: 1, minTimeDelta: 1000 }; + expect(getThrottling(throttling, 100)).toEqual(target100); +}); + +test("Complex throttling config", () => { + // mix ordering + const throttling = [ + { fromNbUser: 100, minDistDelta: 100, maxFreq: 1 }, + { fromNbUser: 0, minDistDelta: 1, maxFreq: 1 }, + { fromNbUser: 50, minDistDelta: 50, maxFreq: 1 }, + ]; + + const target0 = { minDistDelta: 1, minTimeDelta: 1000 }; + expect(getThrottling(throttling, 0)).toEqual(target0); + + const target50 = { minDistDelta: 50, minTimeDelta: 1000 }; + expect(getThrottling(throttling, 50)).toEqual(target50); + + const target100 = { minDistDelta: 100, minTimeDelta: 1000 }; + expect(getThrottling(throttling, 100)).toEqual(target100); +}); diff --git a/src/js/services/InfoService.js b/src/js/services/InfoService.js index 8e5d705..f9a37c9 100644 --- a/src/js/services/InfoService.js +++ b/src/js/services/InfoService.js @@ -14,9 +14,9 @@ class InfoService { * Holds the number of user connected to the server * * @type {number} - * @private + * @readonly */ - _nbConnectedUsers = 0; + nbConnectedUsers = -1; /** * @@ -49,7 +49,11 @@ class InfoService { * @param {{w: number, h: number}} smallestScreenResolution */ updateInfoFromServer({ nbConnectedUsers, smallestScreenResolution = undefined }) { - this._nbConnectedUsers = nbConnectedUsers; + if (this.nbConnectedUsers !== nbConnectedUsers) { + // Refresh config service parameters on nb connected user change + ConfigService.refreshNbUserDependant(nbConnectedUsers); + } + this.nbConnectedUsers = nbConnectedUsers; if (smallestScreenResolution) { this._smallestScreenResolution = smallestScreenResolution; } @@ -73,7 +77,7 @@ class InfoService { refreshDisplayedInfo() { $("#messageReceivedCount")[0].innerText = String(this._nbMessagesReceived); $("#messageSentCount")[0].innerText = String(this._nbMessagesSent); - $("#connectedUsersCount")[0].innerText = String(this._nbConnectedUsers); + $("#connectedUsersCount")[0].innerText = String(this.nbConnectedUsers); const { _smallestScreenResolution: ssr } = this; $("#smallestScreenResolution")[0].innerText = ssr ? `(${ssr.w}, ${ssr.h})` : "Unknown"; } diff --git a/src/js/services/ThrottlingService.js b/src/js/services/ThrottlingService.js index 227e78d..2fa2408 100644 --- a/src/js/services/ThrottlingService.js +++ b/src/js/services/ThrottlingService.js @@ -25,10 +25,10 @@ class ThrottlingService { throttle(newPosition, onSuccess) { const newTime = getCurrentTimeMs(); const { _lastPointPosition, _lastSuccessTime } = this; - if (newTime - _lastSuccessTime > ConfigService.pointerEventsThresholdMinTimeDelta) { + if (newTime - _lastSuccessTime > ConfigService.pointerEventsThrottling.minTimeDelta) { if ( _lastPointPosition.distTo(newPosition) > - ConfigService.pointerEventsThresholdMinDistDelta + ConfigService.pointerEventsThrottling.minDistDelta ) { onSuccess(); this._lastPointPosition = newPosition;