From ef4f1d6f9e42ccde13252edcc853d337aa234682 Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Thu, 8 Jun 2017 04:04:24 +0200 Subject: [PATCH 1/7] add control.update(), which takes a partial control This allows developers to make a batch update to the entire control. --- src/state/Scene.ts | 2 +- src/state/controls/Button.ts | 7 +++++++ src/state/controls/Control.spec.ts | 23 +++++++++++++++++++++++ src/state/controls/Control.ts | 15 +++++++++++++-- src/state/interfaces/controls/IButton.ts | 1 + src/state/interfaces/controls/IControl.ts | 2 +- 6 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/state/Scene.ts b/src/state/Scene.ts index 2e7d021..e1153f8 100644 --- a/src/state/Scene.ts +++ b/src/state/Scene.ts @@ -82,7 +82,7 @@ export class Scene extends EventEmitter implements IScene { private onControlUpdated(controlData: IControlData) { const control = this.getControl(controlData.controlID); if (control) { - control.update(controlData); + control.onUpdate(controlData); } } /** diff --git a/src/state/controls/Button.ts b/src/state/controls/Button.ts index b3e2209..43f16cc 100644 --- a/src/state/controls/Button.ts +++ b/src/state/controls/Button.ts @@ -71,4 +71,11 @@ export class Button extends Control implements IButton { public giveInput(input: IButtonInput): Promise { return this.sendInput(input); } + + public update(changedData: Partial): Promise { + if (changedData.cooldown) { + changedData.cooldown = this.client.state.synchronizeLocalTime().getTime() + changedData.cooldown; + } + return super.update(changedData); + } } diff --git a/src/state/controls/Control.spec.ts b/src/state/controls/Control.spec.ts index 401f17d..9df6095 100644 --- a/src/state/controls/Control.spec.ts +++ b/src/state/controls/Control.spec.ts @@ -58,4 +58,27 @@ describe('control', () => { }); stub.restore(); }); + + it('allows batch updates', () => { + const buttonDiff = { + cost: 200, + text: 'foobar', + }; + const updatedButton = Object.assign( + {}, + { + etag: buttonData.etag, + controlID: buttonData.controlID, + }, + buttonDiff, + ); + const stub = sinon.stub(mockClient, 'updateControls'); + control.update(buttonDiff); + expect(stub).to.be + .calledWith({ + sceneID: 'default', + controls: [updatedButton], + }); + stub.restore(); + }); }); diff --git a/src/state/controls/Control.ts b/src/state/controls/Control.ts index 153336d..76829b8 100644 --- a/src/state/controls/Control.ts +++ b/src/state/controls/Control.ts @@ -93,13 +93,24 @@ export abstract class Control extends EventEmitter imple } /** - * Merges in values from the server in response to an update operation. + * Merges in values from the server in response to an update operation from the server. */ - public update(controlData: IControlData) { + public onUpdate(controlData: IControlData) { merge(this, controlData); this.emit('updated', this); } + public update(changedData: Partial): Promise { + // These must be present and correct at the time of the update, + // Use values we have, not values given. + changedData.controlID = this.controlID; + changedData.etag = this.etag; + return this.client.updateControls({ + sceneID: this.scene.sceneID, + controls: [ changedData ], + }); + } + public destroy(): void { this.emit('deleted', this); } diff --git a/src/state/interfaces/controls/IButton.ts b/src/state/interfaces/controls/IButton.ts index 4d89b8d..dacabce 100644 --- a/src/state/interfaces/controls/IButton.ts +++ b/src/state/interfaces/controls/IButton.ts @@ -39,6 +39,7 @@ export interface IButton extends IControl, IButtonData { setProgress(progress: number): Promise; setCooldown(duration: number): Promise; setCost(cost: number): Promise; + update(changedData: Partial): Promise; /** * Fired when a participant presses this button. diff --git a/src/state/interfaces/controls/IControl.ts b/src/state/interfaces/controls/IControl.ts index 19f41b0..4f227da 100644 --- a/src/state/interfaces/controls/IControl.ts +++ b/src/state/interfaces/controls/IControl.ts @@ -69,7 +69,7 @@ export interface IControl extends IControlData, EventEmitter { /** * Merges in updated control data from the mediator */ - update(controlData: IControlData): void; + onUpdate(controlData: IControlData): void; /** * Fired when the control is deleted. From 22b5ec409a264d35748ea33583792bfbb4e79f0a Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Thu, 8 Jun 2017 04:26:15 +0200 Subject: [PATCH 2/7] PR Comments from Connor --- src/state/controls/Control.spec.ts | 9 ++++----- src/state/controls/Control.ts | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/state/controls/Control.spec.ts b/src/state/controls/Control.spec.ts index 9df6095..296e4f5 100644 --- a/src/state/controls/Control.spec.ts +++ b/src/state/controls/Control.spec.ts @@ -64,14 +64,13 @@ describe('control', () => { cost: 200, text: 'foobar', }; - const updatedButton = Object.assign( - {}, - { + const updatedButton = { + ...{ etag: buttonData.etag, controlID: buttonData.controlID, }, - buttonDiff, - ); + ...buttonDiff, + }; const stub = sinon.stub(mockClient, 'updateControls'); control.update(buttonDiff); expect(stub).to.be diff --git a/src/state/controls/Control.ts b/src/state/controls/Control.ts index 76829b8..db86b3d 100644 --- a/src/state/controls/Control.ts +++ b/src/state/controls/Control.ts @@ -107,7 +107,7 @@ export abstract class Control extends EventEmitter imple changedData.etag = this.etag; return this.client.updateControls({ sceneID: this.scene.sceneID, - controls: [ changedData ], + controls: [changedData], }); } From fc23d55801c3ef305b01982e24584ceec1b3961b Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Thu, 8 Jun 2017 05:32:48 +0200 Subject: [PATCH 3/7] Restructure stuff --- src/state/controls/Button.ts | 11 +++++--- src/state/controls/Control.spec.ts | 3 ++- src/state/controls/Control.ts | 14 +++++++--- src/state/interfaces/controls/IButton.ts | 31 ++++++++++++++++++++-- src/state/interfaces/controls/IControl.ts | 23 ++++++++++++---- src/state/interfaces/controls/IJoystick.ts | 29 +++++++++++++++++++- 6 files changed, 96 insertions(+), 15 deletions(-) diff --git a/src/state/controls/Button.ts b/src/state/controls/Button.ts index 43f16cc..1fbe5f7 100644 --- a/src/state/controls/Button.ts +++ b/src/state/controls/Button.ts @@ -1,4 +1,4 @@ -import { IButton, IButtonData } from '../interfaces/controls/IButton'; +import { IButton, IButtonData, IButtonUpdate } from '../interfaces/controls/IButton'; import { IButtonInput } from '../interfaces/controls/IInput'; import { Control } from './Control'; @@ -60,7 +60,7 @@ export class Button extends Control implements IButton { /** * Sets the spark cost for this button. * An Integer greater than 0 - */ + */s public setCost(cost: number): Promise { return this.updateAttribute('cost', cost); } @@ -72,7 +72,12 @@ export class Button extends Control implements IButton { return this.sendInput(input); } - public update(changedData: Partial): Promise { + /** + * Update this button on the server. + */ + public update(controlUpdate: IButtonUpdate): Promise { + // Clone to prevent mutations + const changedData = {...controlUpdate}; if (changedData.cooldown) { changedData.cooldown = this.client.state.synchronizeLocalTime().getTime() + changedData.cooldown; } diff --git a/src/state/controls/Control.spec.ts b/src/state/controls/Control.spec.ts index 296e4f5..04bf603 100644 --- a/src/state/controls/Control.spec.ts +++ b/src/state/controls/Control.spec.ts @@ -2,6 +2,7 @@ import { expect, use } from 'chai'; import * as sinon from 'sinon'; import { Client, ClientType } from '../../Client'; +import { IButtonUpdate } from '../interfaces/controls'; import { Scene } from '../Scene'; import { Button } from './'; @@ -60,7 +61,7 @@ describe('control', () => { }); it('allows batch updates', () => { - const buttonDiff = { + const buttonDiff: IButtonUpdate = { cost: 200, text: 'foobar', }; diff --git a/src/state/controls/Control.ts b/src/state/controls/Control.ts index db86b3d..629c5a0 100644 --- a/src/state/controls/Control.ts +++ b/src/state/controls/Control.ts @@ -7,6 +7,7 @@ import { ControlKind, IControl, IControlData, + IControlUpdate, IGridPlacement, } from '../interfaces/controls/IControl'; import { IInput, IInputEvent } from '../interfaces/controls/IInput'; @@ -100,11 +101,18 @@ export abstract class Control extends EventEmitter imple this.emit('updated', this); } - public update(changedData: Partial): Promise { + /** + * Update this control on the server. + */ + public update(controlUpdate: IControlUpdate): Promise { // These must be present and correct at the time of the update, // Use values we have, not values given. - changedData.controlID = this.controlID; - changedData.etag = this.etag; + const changedData = { + ...controlUpdate, + controlID: this.controlID, + etag: this.etag, + }; + return this.client.updateControls({ sceneID: this.scene.sceneID, controls: [changedData], diff --git a/src/state/interfaces/controls/IButton.ts b/src/state/interfaces/controls/IButton.ts index dacabce..d66a281 100644 --- a/src/state/interfaces/controls/IButton.ts +++ b/src/state/interfaces/controls/IButton.ts @@ -1,5 +1,5 @@ import { IParticipant } from '../'; -import { IControl, IControlData } from './IControl'; +import { IControl, IControlData, IControlUpdate } from './IControl'; import { IButtonInput, IInputEvent } from './IInput'; /** @@ -28,6 +28,33 @@ export interface IButtonData extends IControlData { keyCode?: number; } +/** + * Represents updatable components of a button which developers can update + * from game clients. + */ +export interface IButtonUpdate extends IControlUpdate { + /** + * Will update the text of this button. + */ + text?: string; + /** + * In milliseconds, will be converted to a unix timestamp of when this cooldown expires. + */ + cooldown?: number; + /** + * Will update the spark cost of this button. + */ + cost?: number; + /** + * Will update the progress bar underneath the button. 0 - 1. + */ + progress?: number; + /** + * Will update the keycode used by participants for keyboard control. + */ + keyCode?: number; +} + export interface IButton extends IControl, IButtonData { text: string; cost: number; @@ -39,7 +66,7 @@ export interface IButton extends IControl, IButtonData { setProgress(progress: number): Promise; setCooldown(duration: number): Promise; setCost(cost: number): Promise; - update(changedData: Partial): Promise; + update(changedData: IButtonUpdate): Promise; /** * Fired when a participant presses this button. diff --git a/src/state/interfaces/controls/IControl.ts b/src/state/interfaces/controls/IControl.ts index 4f227da..b1b9b7d 100644 --- a/src/state/interfaces/controls/IControl.ts +++ b/src/state/interfaces/controls/IControl.ts @@ -2,7 +2,6 @@ import { EventEmitter } from 'events'; import { ETag, IParticipant } from '../'; import { IClient } from '../../../IClient'; -import { ISceneDataArray } from '../IScene'; import { IInput, IInputEvent } from './IInput'; import { IMeta } from './IMeta'; @@ -40,6 +39,19 @@ export interface IControlData { */ etag?: ETag; } + +/** + * Represents updatable components of a control which developers can update + * from game clients. + */ +export interface IControlUpdate { + /** + * When set to true this will disable the control. + * When set to false this will enable the control. + */ + disabled?: boolean; +} + /** * Control is used a base class for all other controls within an interactive session. * It contains shared logic which all types of controls can utilize. @@ -71,6 +83,11 @@ export interface IControl extends IControlData, EventEmitter { */ onUpdate(controlData: IControlData): void; + /** + * Updates the control with the supplied update parameters + */ + update(controlUpdate: IControlUpdate): Promise; + /** * Fired when the control is deleted. */ @@ -113,7 +130,3 @@ export interface IGridPlacement { */ y: number; } - -export interface IControlUpdate { - scenes: ISceneDataArray; -} diff --git a/src/state/interfaces/controls/IJoystick.ts b/src/state/interfaces/controls/IJoystick.ts index b19fb9f..89ddd3a 100644 --- a/src/state/interfaces/controls/IJoystick.ts +++ b/src/state/interfaces/controls/IJoystick.ts @@ -1,5 +1,5 @@ import { IParticipant } from '../'; -import { IControl, IControlData } from './IControl'; +import { IControl, IControlData, IControlUpdate } from './IControl'; import { IInputEvent, IJoystickInput } from './IInput'; /** @@ -24,6 +24,28 @@ export interface IJoystickData extends IControlData { sampleRate?: number; } +/** + * Represents updatable components of a joystick which developers can update + * from game clients. + */ +export interface IJoystickUpdate extends IControlUpdate { + /** + * Updates the angle of the Joysticks direction indicator. + * In radians 0 - 2. + */ + angle?: number; + /** + * updates the strength/opacity of the direction indicator. + */ + intensity?: number; + /** + * Updates the sampleRate of this joystick + * + * In milliseconds. + */ + sampleRate?: number; +} + /** * A joysticks coordinates. * @@ -48,6 +70,11 @@ export interface IJoystick extends IControl, IJoystickData { */ setIntensity(intensity: number): Promise; + /** + * Updates the joystick with the supplied joystick parameters + */ + update(controlUpdate: IJoystickUpdate): Promise; + /** * Fired when a participant moves this joystick. */ From 3e56a27bf15124dda0f065be9c7c9e28ac07c8a3 Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Thu, 8 Jun 2017 05:34:16 +0200 Subject: [PATCH 4/7] ss --- src/state/controls/Button.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/state/controls/Button.ts b/src/state/controls/Button.ts index 1fbe5f7..d7c8386 100644 --- a/src/state/controls/Button.ts +++ b/src/state/controls/Button.ts @@ -60,7 +60,7 @@ export class Button extends Control implements IButton { /** * Sets the spark cost for this button. * An Integer greater than 0 - */s + */ public setCost(cost: number): Promise { return this.updateAttribute('cost', cost); } From 4b9a32f595b72b2eb0419189a3ea1836e570a7ff Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Thu, 8 Jun 2017 05:36:42 +0200 Subject: [PATCH 5/7] This dont make sense now --- src/state/controls/Control.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/state/controls/Control.ts b/src/state/controls/Control.ts index 629c5a0..f719d53 100644 --- a/src/state/controls/Control.ts +++ b/src/state/controls/Control.ts @@ -105,8 +105,6 @@ export abstract class Control extends EventEmitter imple * Update this control on the server. */ public update(controlUpdate: IControlUpdate): Promise { - // These must be present and correct at the time of the update, - // Use values we have, not values given. const changedData = { ...controlUpdate, controlID: this.controlID, From 83e49c00f86e5c236758482af818bd8c7a7eb12a Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Thu, 8 Jun 2017 14:49:15 +0200 Subject: [PATCH 6/7] Ok how about generics --- src/state/controls/Control.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/state/controls/Control.ts b/src/state/controls/Control.ts index f719d53..9832cb6 100644 --- a/src/state/controls/Control.ts +++ b/src/state/controls/Control.ts @@ -104,9 +104,9 @@ export abstract class Control extends EventEmitter imple /** * Update this control on the server. */ - public update(controlUpdate: IControlUpdate): Promise { + public update(controlUpdate: T): Promise { const changedData = { - ...controlUpdate, + ...controlUpdate, controlID: this.controlID, etag: this.etag, }; From 20c72d7e06973b3076156e07f355c87ae5285c71 Mon Sep 17 00:00:00 2001 From: ProbablePrime Date: Thu, 8 Jun 2017 15:38:41 +0200 Subject: [PATCH 7/7] Birtles fixes --- src/state/controls/Control.spec.ts | 6 ++---- src/state/controls/Control.ts | 2 +- src/state/interfaces/controls/IJoystick.ts | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/state/controls/Control.spec.ts b/src/state/controls/Control.spec.ts index 04bf603..6c83a35 100644 --- a/src/state/controls/Control.spec.ts +++ b/src/state/controls/Control.spec.ts @@ -66,10 +66,8 @@ describe('control', () => { text: 'foobar', }; const updatedButton = { - ...{ - etag: buttonData.etag, - controlID: buttonData.controlID, - }, + etag: buttonData.etag, + controlID: buttonData.controlID, ...buttonDiff, }; const stub = sinon.stub(mockClient, 'updateControls'); diff --git a/src/state/controls/Control.ts b/src/state/controls/Control.ts index 9832cb6..906f74c 100644 --- a/src/state/controls/Control.ts +++ b/src/state/controls/Control.ts @@ -104,7 +104,7 @@ export abstract class Control extends EventEmitter imple /** * Update this control on the server. */ - public update(controlUpdate: T): Promise { + public update(controlUpdate: T2): Promise { const changedData = { ...controlUpdate, controlID: this.controlID, diff --git a/src/state/interfaces/controls/IJoystick.ts b/src/state/interfaces/controls/IJoystick.ts index 89ddd3a..76e49d0 100644 --- a/src/state/interfaces/controls/IJoystick.ts +++ b/src/state/interfaces/controls/IJoystick.ts @@ -8,7 +8,7 @@ import { IInputEvent, IJoystickInput } from './IInput'; export interface IJoystickData extends IControlData { /** * The angle of the Joysticks direction indicator. - * In radians 0 - 2. + * In radians 0 - 2π. */ angle?: number; /** @@ -31,7 +31,7 @@ export interface IJoystickData extends IControlData { export interface IJoystickUpdate extends IControlUpdate { /** * Updates the angle of the Joysticks direction indicator. - * In radians 0 - 2. + * In radians 0 - 2π. */ angle?: number; /**