text: "use strict";
  const libErrors = require("@clusterio/lib/errors");
  const libLink = require("@clusterio/lib/link");
  const { logger } = require("@clusterio/lib/logging");
  const libPlugin = require("@clusterio/lib/plugin");
  
  
  /**
   * Base class for master server connections
   *
   * @extends module:lib/link.Link
   * @alias module:master/src/BaseConnection
   */
  class BaseConnection extends libLink.Link {
  	constructor(target, connector, master) {
  		/** @member {module:master/src/WsServerConnector} module:master/src/BaseConnection#connector */
  		super("master", target, connector);
  		this._master = master;
  		this._disconnecting = false;
  		libLink.attachAllMessages(this);
  		for (let masterPlugin of this._master.plugins.values()) {
  			libPlugin.attachPluginMessages(this, masterPlugin);
  		}
  	}
  
  	async forwardRequestToInstance(message, request) {
  		return await this._master.forwardRequestToInstance(request, message.data);
  	}
  
  	async forwardEventToInstance(message, event) {
  		let instance = this._master.instances.get(message.data.instance_id);
  		if (!instance) { return; }
  
  		let slaveId = instance.config.get("instance.assigned_slave");
  		if (slaveId === null) { return; }
  
  		let connection = this._master.wsServer.slaveConnections.get(slaveId);
  		if (!connection || connection.closing) { return; }
  		if (event.plugin && !connection.plugins.has(event.plugin)) { return; }
  
  		event.send(connection, message.data);
  	}
  
  	async broadcastEventToSlaves(message, event) {
  		for (let slaveConnection of this._master.wsServer.slaveConnections.values()) {
  			// Do not broadcast back to the source
  			if (slaveConnection === this) { continue; }
  			if (slaveConnection.connector.closing) { continue; }
  			if (event.plugin && !slaveConnection.plugins.has(event.plugin)) { continue; }
  
  			event.send(slaveConnection, message.data);
  		}
  	}
  
  	async broadcastEventToInstance(message, event) {
  		await this.broadcastEventToSlaves(message, event);
  	}
  
  	async prepareDisconnectRequestHandler(message, request) {
  		await libPlugin.invokeHook(this._master.plugins, "onPrepareSlaveDisconnect", this);
  		this._disconnecting = true;
  		this.connector.setClosing();
  		return await super.prepareDisconnectRequestHandler(message, request);
  	}
  
  	async disconnect(code, reason) {
  		this.connector.setClosing();
  		try {
  			await libLink.messages.prepareDisconnect.send(this);
  		} catch (err) {
  			if (!(err instanceof libErrors.SessionLost)) {
  				logger.error(`"Unexpected error preparing disconnect:\n${err.stack}`);
  			}
  		}
  
  		await this.connector.close(code, reason);
  	}
  
  	/**
  	 * True if the link is connected, not in the dropped state and not in
  	 * the process of disconnecting.
  	 * @type {boolean}
  	 */
  	get connected() {
  		return !this._disconnecting && this.connector.connected;
  	}
  }
  
  module.exports = BaseConnection;
  