TeaWeb/shared/js/connection/AbstractCommandHandler.ts

129 lines
4.7 KiB
TypeScript

import {AbstractServerConnection, ServerCommand, SingleCommandHandler} from "../connection/ConnectionBase";
import {tr} from "../i18n/localize";
import {LogCategory, logError, logWarn} from "tc-shared/log";
export abstract class AbstractCommandHandler {
readonly connection: AbstractServerConnection;
handler_boss: AbstractCommandHandlerBoss | undefined;
volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */
ignore_consumed: boolean = false;
protected constructor(connection: AbstractServerConnection) {
this.connection = connection;
}
/**
* @return If the command should be consumed
*/
abstract handle_command(command: ServerCommand) : boolean;
}
export type CommandHandlerCallback = (command: ServerCommand, consumed: boolean) => void | boolean;
export abstract class AbstractCommandHandlerBoss {
readonly connection: AbstractServerConnection;
protected command_handlers: AbstractCommandHandler[] = [];
/* TODO: Timeout */
protected single_command_handler: SingleCommandHandler[] = [];
protected explicitHandlers: {[key: string]:CommandHandlerCallback[]} = {};
protected constructor(connection: AbstractServerConnection) {
this.connection = connection;
}
destroy() {
this.command_handlers = undefined;
this.single_command_handler = undefined;
}
registerCommandHandler(command: string, callback: CommandHandlerCallback) : () => void {
this.explicitHandlers[command] = this.explicitHandlers[command] || [];
this.explicitHandlers[command].push(callback);
return () => this.explicitHandlers[command].remove(callback);
}
unregisterCommandHandler(command: string, callback: CommandHandlerCallback) {
if(!this.explicitHandlers[command])
return false;
this.explicitHandlers[command].remove(callback);
return true;
}
registerHandler(handler: AbstractCommandHandler) {
if(!handler.volatile_handler_boss && handler.handler_boss) {
throw "handler already registered";
}
this.command_handlers.remove(handler); /* just to be sure */
this.command_handlers.push(handler);
handler.handler_boss = this;
}
unregisterHandler(handler: AbstractCommandHandler) {
if(!handler.volatile_handler_boss && handler.handler_boss !== this) {
logWarn(LogCategory.NETWORKING, tr("Tried to unregister command handler which does not belong to the handler boss"));
return;
}
this.command_handlers.remove(handler);
handler.handler_boss = undefined;
}
registerSingleHandler(handler: SingleCommandHandler) {
if(typeof handler.command === "string")
handler.command = [handler.command];
this.single_command_handler.push(handler);
}
removeSingleHandler(handler: SingleCommandHandler) {
this.single_command_handler.remove(handler);
}
handlers() : AbstractCommandHandler[] {
return this.command_handlers;
}
invokeCommand(command: ServerCommand) : boolean {
let flag_consumed = false;
for(const handler of this.command_handlers) {
try {
if(!flag_consumed || handler.ignore_consumed)
flag_consumed = handler.handle_command(command) || flag_consumed;
} catch(error) {
logError(LogCategory.NETWORKING, tr("Failed to invoke command handler. Invocation results in an exception: %o"), error);
}
}
const explHandlers = this.explicitHandlers[command.command];
if(Array.isArray(explHandlers)) {
for(const handler of explHandlers) {
try {
flag_consumed = handler(command, flag_consumed) || flag_consumed;
} catch(error) {
logError(LogCategory.NETWORKING, tr("Failed to invoke command handler. Invocation results in an exception: %o"), error);
}
}
}
for(const handler of [...this.single_command_handler]) {
// We already know that handler.command must be an array (It will be converted within the register single handler methode)
if(handler.command && (handler.command as string[]).findIndex(e => e === command.command) == -1)
continue;
try {
if(handler.function(command))
this.single_command_handler.remove(handler);
} catch(error) {
logError(LogCategory.NETWORKING, tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error);
}
}
return flag_consumed;
}
}