2023-07-25 11:33:13 +02:00
|
|
|
import * as plugins from './smartuniverse.plugins.js';
|
|
|
|
import { Smartsocket, SmartsocketClient } from '@push.rocks/smartsocket';
|
2018-03-13 06:15:40 +01:00
|
|
|
|
2023-07-25 11:33:13 +02:00
|
|
|
import * as interfaces from './interfaces/index.js';
|
2018-03-20 08:16:54 +01:00
|
|
|
|
2023-07-25 11:33:13 +02:00
|
|
|
import { ClientUniverseChannel, ClientUniverseMessage } from './index.js';
|
|
|
|
import { ClientUniverseCache } from './smartuniverse.classes.client.universecache.js';
|
|
|
|
import { logger } from './smartuniverse.logging.js';
|
2018-03-13 06:15:40 +01:00
|
|
|
|
2018-03-15 01:05:13 +01:00
|
|
|
export interface IClientOptions {
|
2018-03-20 08:16:54 +01:00
|
|
|
serverAddress: string;
|
2019-11-09 12:59:51 +01:00
|
|
|
autoReconnect: boolean;
|
2018-03-15 01:05:13 +01:00
|
|
|
}
|
|
|
|
|
2018-04-13 15:45:48 +02:00
|
|
|
/**
|
|
|
|
* this class is for client side only!!!
|
|
|
|
* allows connecting to a universe server
|
|
|
|
*/
|
2018-05-28 12:07:25 +02:00
|
|
|
export class ClientUniverse {
|
2019-11-09 12:23:33 +01:00
|
|
|
public options: IClientOptions;
|
2019-04-24 23:27:57 +02:00
|
|
|
public smartsocketClient: plugins.smartsocket.SmartsocketClient;
|
2019-11-03 20:23:22 +01:00
|
|
|
public messageRxjsSubject = new plugins.smartrx.rxjs.Subject<ClientUniverseMessage<any>>();
|
2019-04-28 12:42:08 +02:00
|
|
|
public clientUniverseCache = new ClientUniverseCache();
|
2018-03-13 06:15:40 +01:00
|
|
|
|
2019-11-09 18:44:33 +01:00
|
|
|
public autoReconnectStatus: 'on' | 'off' = 'off';
|
|
|
|
|
2018-03-15 01:05:13 +01:00
|
|
|
constructor(optionsArg: IClientOptions) {
|
|
|
|
this.options = optionsArg;
|
|
|
|
}
|
2018-03-20 08:16:54 +01:00
|
|
|
|
2019-04-22 13:06:01 +02:00
|
|
|
/**
|
|
|
|
* adds a channel to the channelcache
|
|
|
|
* TODO: verify channel before adding it to the channel cache
|
|
|
|
*/
|
2019-08-13 18:41:27 +02:00
|
|
|
public addChannel(channelNameArg: string, passphraseArg: string) {
|
|
|
|
const existingChannel = this.getChannel(channelNameArg);
|
2019-04-22 22:04:52 +02:00
|
|
|
|
|
|
|
if (existingChannel) {
|
|
|
|
throw new Error('channel exists');
|
|
|
|
}
|
|
|
|
|
2019-04-22 23:11:51 +02:00
|
|
|
// lets create the channel
|
2019-09-01 17:04:25 +02:00
|
|
|
const clientUniverseChannel = ClientUniverseChannel.createClientUniverseChannel(
|
|
|
|
this,
|
|
|
|
channelNameArg,
|
|
|
|
passphraseArg
|
|
|
|
);
|
2019-08-13 18:41:27 +02:00
|
|
|
return clientUniverseChannel;
|
2019-04-22 13:06:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gets a channel from the channelcache
|
|
|
|
* @param channelName
|
2019-04-23 00:28:57 +02:00
|
|
|
* @param passphraseArg
|
2019-04-22 13:06:01 +02:00
|
|
|
*/
|
2019-08-13 18:41:27 +02:00
|
|
|
public getChannel(channelName: string): ClientUniverseChannel {
|
2023-07-25 11:33:13 +02:00
|
|
|
const clientUniverseChannel = this.clientUniverseCache.channelMap.findSync((channel) => {
|
2019-04-22 22:04:52 +02:00
|
|
|
return channel.name === channelName;
|
2019-04-23 00:28:57 +02:00
|
|
|
});
|
2019-04-22 13:06:01 +02:00
|
|
|
return clientUniverseChannel;
|
|
|
|
}
|
|
|
|
|
2019-04-28 12:42:08 +02:00
|
|
|
/**
|
|
|
|
* remove a a achannel
|
|
|
|
* @param messageArg
|
|
|
|
*/
|
|
|
|
public removeChannel(channelNameArg, notifyServer = true) {
|
2023-07-25 11:33:13 +02:00
|
|
|
const clientUniverseChannel = this.clientUniverseCache.channelMap.findOneAndRemoveSync(
|
2020-09-24 18:17:52 +00:00
|
|
|
(channelItemArg) => {
|
2019-09-01 17:04:25 +02:00
|
|
|
return channelItemArg.name === channelNameArg;
|
|
|
|
}
|
|
|
|
);
|
2019-04-28 12:42:08 +02:00
|
|
|
}
|
|
|
|
|
2019-08-13 15:48:20 +02:00
|
|
|
public async start() {
|
2019-11-09 18:44:33 +01:00
|
|
|
if (this.options.autoReconnect) {
|
|
|
|
this.autoReconnectStatus = 'on';
|
|
|
|
}
|
2019-08-13 15:48:20 +02:00
|
|
|
await this.checkConnection();
|
|
|
|
}
|
|
|
|
|
2019-09-10 00:39:18 +02:00
|
|
|
public async stop() {
|
2019-11-09 18:44:33 +01:00
|
|
|
this.autoReconnectStatus = 'off';
|
2019-11-09 12:23:33 +01:00
|
|
|
await this.disconnect('triggered');
|
2018-05-28 12:07:25 +02:00
|
|
|
}
|
|
|
|
|
2019-04-22 13:06:01 +02:00
|
|
|
/**
|
|
|
|
* checks the connection towards a universe server
|
|
|
|
* since password validation is done through other means, a connection should always be possible
|
|
|
|
*/
|
2019-11-09 18:44:33 +01:00
|
|
|
private async checkConnection(): Promise<void> {
|
2019-11-03 20:23:22 +01:00
|
|
|
if (!this.smartsocketClient) {
|
2023-07-25 11:33:13 +02:00
|
|
|
const parsedURL = plugins.smarturl.Smarturl.createFromUrl(this.options.serverAddress);
|
2019-04-24 18:20:31 +02:00
|
|
|
const socketConfig: plugins.smartsocket.ISmartsocketClientOptions = {
|
2019-09-01 21:34:01 +02:00
|
|
|
alias: 'universeclient',
|
2018-03-20 08:16:54 +01:00
|
|
|
port: parseInt(parsedURL.port, 10),
|
2020-09-24 18:17:52 +00:00
|
|
|
url: parsedURL.protocol + '//' + parsedURL.hostname,
|
2019-04-24 18:20:31 +02:00
|
|
|
};
|
2019-04-24 23:27:57 +02:00
|
|
|
this.smartsocketClient = new SmartsocketClient(socketConfig);
|
|
|
|
|
2020-09-24 18:17:52 +00:00
|
|
|
this.smartsocketClient.eventSubject.subscribe(async (eventArg) => {
|
2019-11-09 13:00:30 +01:00
|
|
|
switch (eventArg) {
|
2019-11-09 12:23:33 +01:00
|
|
|
case 'disconnected':
|
|
|
|
this.disconnect('upstreamEvent');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-04-24 23:27:57 +02:00
|
|
|
// lets define some basic actions
|
|
|
|
|
|
|
|
/**
|
|
|
|
* should handle a forced unsubscription by the server
|
|
|
|
*/
|
2019-08-13 15:48:20 +02:00
|
|
|
const socketFunctionUnsubscribe = new plugins.smartsocket.SocketFunction({
|
2019-04-24 23:27:57 +02:00
|
|
|
funcName: 'unsubscribe',
|
2019-11-03 20:23:22 +01:00
|
|
|
funcDef: async (dataArg: interfaces.IServerUnsubscribeActionPayload) => {
|
2023-07-25 11:33:13 +02:00
|
|
|
const channel = this.clientUniverseCache.channelMap.findSync((channelArg) => {
|
2019-11-03 20:23:22 +01:00
|
|
|
return channelArg.name === dataArg.name;
|
|
|
|
});
|
|
|
|
if (channel) {
|
|
|
|
channel.unsubscribe();
|
|
|
|
}
|
|
|
|
return {};
|
2020-09-24 18:17:52 +00:00
|
|
|
},
|
2019-04-24 23:27:57 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
2019-08-13 15:48:20 +02:00
|
|
|
* handles message reception
|
2019-04-24 23:27:57 +02:00
|
|
|
*/
|
2023-07-25 11:33:13 +02:00
|
|
|
const socketFunctionProcessMessage =
|
|
|
|
new plugins.smartsocket.SocketFunction<interfaces.ISocketRequest_ProcessMessage>({
|
|
|
|
funcName: 'processMessage',
|
|
|
|
funcDef: async (messageDescriptorArg) => {
|
|
|
|
logger.log('info', 'Got message from server');
|
|
|
|
const clientUniverseMessage =
|
|
|
|
ClientUniverseMessage.createMessageFromMessageDescriptor(messageDescriptorArg);
|
|
|
|
this.messageRxjsSubject.next(clientUniverseMessage);
|
|
|
|
|
|
|
|
// lets find the corresponding channel
|
|
|
|
const targetChannel = this.getChannel(clientUniverseMessage.targetChannelName);
|
|
|
|
if (targetChannel) {
|
|
|
|
await targetChannel.emitMessageLocally(clientUniverseMessage);
|
|
|
|
return {
|
|
|
|
messageStatus: 'ok',
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return {
|
|
|
|
messageStatus: 'channel not found',
|
|
|
|
};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
2019-04-24 23:27:57 +02:00
|
|
|
|
2019-08-13 15:48:20 +02:00
|
|
|
// add functions
|
|
|
|
this.smartsocketClient.addSocketFunction(socketFunctionUnsubscribe);
|
|
|
|
this.smartsocketClient.addSocketFunction(socketFunctionProcessMessage);
|
|
|
|
|
2019-04-24 23:27:57 +02:00
|
|
|
await this.smartsocketClient.connect();
|
2020-09-24 18:13:48 +00:00
|
|
|
logger.log('info', 'universe client connected successfully');
|
2020-09-24 18:17:52 +00:00
|
|
|
await this.clientUniverseCache.channelMap.forEach(async (clientUniverseChannelArg) => {
|
2019-09-10 19:36:10 +02:00
|
|
|
await clientUniverseChannelArg.populateSubscriptionToServer();
|
2019-08-13 15:48:20 +02:00
|
|
|
});
|
2018-03-20 08:16:54 +01:00
|
|
|
}
|
2018-03-28 00:38:32 +02:00
|
|
|
}
|
2019-11-09 12:23:33 +01:00
|
|
|
|
2019-11-09 18:44:33 +01:00
|
|
|
private async disconnect(
|
2019-11-09 13:00:30 +01:00
|
|
|
reason: 'upstreamEvent' | 'triggered' = 'triggered',
|
|
|
|
tryReconnect = false
|
|
|
|
) {
|
2019-11-09 18:44:33 +01:00
|
|
|
const instructDisconnect = async () => {
|
|
|
|
if (this.smartsocketClient) {
|
|
|
|
const smartsocketToDisconnect = this.smartsocketClient;
|
|
|
|
this.smartsocketClient = null; // making sure the upstreamEvent does not interfere
|
|
|
|
await smartsocketToDisconnect.disconnect();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if (reason === 'triggered' && this.smartsocketClient) {
|
|
|
|
await instructDisconnect();
|
2019-11-09 12:23:33 +01:00
|
|
|
}
|
2019-11-09 18:44:33 +01:00
|
|
|
if (this.autoReconnectStatus === 'on' && reason === 'upstreamEvent') {
|
|
|
|
await instructDisconnect();
|
2019-11-09 12:23:33 +01:00
|
|
|
await plugins.smartdelay.delayForRandom(5000, 20000);
|
2019-11-09 18:44:33 +01:00
|
|
|
await this.checkConnection();
|
2019-11-09 12:23:33 +01:00
|
|
|
}
|
|
|
|
}
|
2018-03-15 01:05:13 +01:00
|
|
|
}
|