diff --git a/packages/lib/src/subscriptions.ts b/packages/lib/src/subscriptions.ts index 5cda0e02..0cd427cc 100644 --- a/packages/lib/src/subscriptions.ts +++ b/packages/lib/src/subscriptions.ts @@ -1,5 +1,6 @@ import { Type, Static } from "@sinclair/typebox"; import { Link, Event, EventClass, RequestHandler, WebSocketClientConnector, WebSocketBaseConnector } from "./link"; +import { logger } from "./logging"; import { Address, MessageRequest, IControllerUser } from "./data"; export type SubscriptionRequestHandler = RequestHandler | null>; @@ -157,20 +158,16 @@ export class EventSubscriber, V=T> { _callbacks = new Array>(); lastResponse: Event | null = null; lastResponseTime = -1; - control?: Link; constructor( - private event: EventClass, + private Event: EventClass, + public control: Link, private prehandler?: (e: T) => V, - control?: Link ) { - const entry = Link._eventsByClass.get(this.event); - if (!entry) { - throw new Error(`Unregistered Event class ${this.event.name}`); - } - if (control) { - this.connectControl(control); - } + control.handle(Event, this._handle.bind(this)); + control.connector.on("connect", () => { + this._updateSubscription(); + }); } /** @@ -187,20 +184,6 @@ export class EventSubscriber, V=T> { } } - /** - * If a control link was not connected at creation, it can be connected - * here (normally during onControllerConnectionEvent) - * @param control - Control link to associate with. - */ - async connectControl(control: Link) { - if (this.control === control) { - return; - } - this.control = control; - this.control.handle(this.event, this._handle.bind(this)); - await this._updateSubscription(); - } - /** * Subscribe to receive all event notifications * @param handler - @@ -235,15 +218,21 @@ export class EventSubscriber, V=T> { * Update the subscription with the controller based on current handler counts */ async _updateSubscription() { - if (!this.control || !(this.control.connector as WebSocketClientConnector).connected) { + if (!(this.control.connector as WebSocketClientConnector).connected) { return; } - const entry = Link._eventsByClass.get(this.event)!; + const entry = Link._eventsByClass.get(this.Event)!; - await this.control.send(new SubscriptionRequest( - entry.name, - this._callbacks.length > 0, - this.lastResponseTime - )); + try { + await this.control.send(new SubscriptionRequest( + entry.name, + this._callbacks.length > 0, + this.lastResponseTime + )); + } catch (err: any) { + logger.error( + `Unexpected error updating ${entry.name} subscription:\n${err.stack}` + ); + } } } diff --git a/packages/web_ui/src/util/websocket.ts b/packages/web_ui/src/util/websocket.ts index 20cd5ed8..cb7f4320 100644 --- a/packages/web_ui/src/util/websocket.ts +++ b/packages/web_ui/src/util/websocket.ts @@ -61,27 +61,27 @@ export class Control extends lib.Link { /** Updates handled by the subscription service */ hostUpdate = new lib.EventSubscriber( - lib.HostUpdatesEvent, event => event.updates, + lib.HostUpdatesEvent, this, event => event.updates, ); instanceUpdate = new lib.EventSubscriber( - lib.InstanceDetailsUpdatesEvent, event => event.updates, + lib.InstanceDetailsUpdatesEvent, this, event => event.updates, ); saveListUpdate = new lib.EventSubscriber( - lib.InstanceSaveDetailsUpdatesEvent, event => event.updates, + lib.InstanceSaveDetailsUpdatesEvent, this, event => event.updates, ); modPackUpdate = new lib.EventSubscriber( - lib.ModPackUpdatesEvent, event => event.updates, + lib.ModPackUpdatesEvent, this, event => event.updates, ); modUpdate = new lib.EventSubscriber( - lib.ModUpdatesEvent, event => event.updates, + lib.ModUpdatesEvent, this, event => event.updates, ); userUpdate = new lib.EventSubscriber( - lib.UserUpdatesEvent, event => event.updates, + lib.UserUpdatesEvent, this, event => event.updates, ); declare connector: ControlConnector; @@ -95,24 +95,6 @@ export class Control extends lib.Link { this.accountName = data.account.name; this.accountRoles = data.account.roles; this.emitAccountUpdate(); - this.hostUpdate.connectControl(this).catch(err => logger.error( - `Unexpected error updating host subscriptions:\n${err.stack}` - )); - this.instanceUpdate.connectControl(this).catch(err => logger.error( - `Unexpected error updating instance subscriptions:\n${err.stack}` - )); - this.saveListUpdate.connectControl(this).catch(err => logger.error( - `Unexpected error updating save list subscriptions:\n${err.stack}` - )); - this.modPackUpdate.connectControl(this).catch(err => logger.error( - `Unexpected error updating mod pack subscriptions:\n${err.stack}` - )); - this.modUpdate.connectControl(this).catch(err => logger.error( - `Unexpected error updating mod subscriptions:\n${err.stack}` - )); - this.userUpdate.connectControl(this).catch(err => logger.error( - `Unexpected error updating user subscriptions:\n${err.stack}` - )); this.updateLogSubscriptions().catch(err => logger.error( `Unexpected error updating log subscriptions:\n${err.stack}` )); diff --git a/test/lib/subscriptions.js b/test/lib/subscriptions.js index 5095cc5c..ec46a846 100644 --- a/test/lib/subscriptions.js +++ b/test/lib/subscriptions.js @@ -303,7 +303,7 @@ describe("lib/subscriptions", function() { addr({ controlId: 0 }), addr("controller"), )); - registeredEvent = new lib.EventSubscriber(RegisteredEvent, undefined, mockControl); + registeredEvent = new lib.EventSubscriber(RegisteredEvent, mockControl); }); function assertLastRequest(request) { @@ -317,36 +317,23 @@ describe("lib/subscriptions", function() { describe("constructor()", function() { it("should not accept unregistered events", function() { assert.throws( - () => new lib.EventSubscriber(UnregisteredEvent), + () => new lib.EventSubscriber(UnregisteredEvent, mockControl), new Error(`Unregistered Event class ${UnregisteredEvent.name}`) ); }); - it("should handle the provided event, if a control link is provided", function() { + it("should handle the provided event", function() { assert.equal(mockControl._eventHandlers.has(RegisteredEvent), true); }); it("should call and use the return of a pre-handler, if provided", function() { let calledWith = null; function prehandler() { return "PreHandlerReturn"; } - const eventSubscriber = new lib.EventSubscriber(StringPermissionEvent, prehandler, mockControl); + const eventSubscriber = new lib.EventSubscriber(StringPermissionEvent, mockControl, prehandler); eventSubscriber.subscribe(function(value) { calledWith = value; }); eventSubscriber._handle(new StringPermissionEvent()); assert.equal(calledWith, "PreHandlerReturn"); }); }); - describe("connectControl()", function() { - it("should handle the provided event", function() { - const control = new MockControl(new MockConnector(addr({ controlId: 0 }), addr("controller"))); - registeredEvent.connectControl(control); - assert.equal(control._eventHandlers.has(RegisteredEvent), true); - }); - it("should do nothing if the control is already connected", function() { - assert.equal(mockControl._eventHandlers.has(RegisteredEvent), true); - registeredEvent.connectControl(mockControl); - assert.equal(mockControl._eventHandlers.has(RegisteredEvent), true); - }); - }); - describe("subscribe()", function() { it("should allow subscriptions to an event", function() { let calledWith = null;