From 14f474af0017dd1c922f53dad5c40d15d0a58ec9 Mon Sep 17 00:00:00 2001 From: myst Date: Sat, 23 Nov 2024 23:50:58 +0100 Subject: [PATCH 1/2] feat(backend): added TM implementation feat(frontend): answer TM requests --- backend/src/core/sockets/socket-manager.ts | 12 ++++++++++++ .../core/sockets/types/client-to-server-events.ts | 1 + .../core/sockets/types/server-to-client-events.ts | 1 + backend/src/features/telnet/README.md | 2 +- backend/src/features/telnet/telnet-client.ts | 10 ++++++++++ frontend/src/app/features/sockets/sockets.service.ts | 10 ++++++++++ .../sockets/types/client-to-server-events.ts | 1 + .../sockets/types/server-to-client-events.ts | 1 + 8 files changed, 37 insertions(+), 1 deletion(-) diff --git a/backend/src/core/sockets/socket-manager.ts b/backend/src/core/sockets/socket-manager.ts index d4c8547d..d4534449 100644 --- a/backend/src/core/sockets/socket-manager.ts +++ b/backend/src/core/sockets/socket-manager.ts @@ -108,6 +108,10 @@ export class SocketManager extends Server< }, Environment.getInstance().socketTimeout); }); + socket.on('answerTimingMark', () => { + this.mudConnections[socket.id].telnet?.sendTimingMark(); + }); + socket.on('mudInput', (data: string) => { const inputEcho = this.mudConnections[socket.id].echo; @@ -208,6 +212,14 @@ export class SocketManager extends Server< socket.emit('setEchoMode', true); } + + break; + } + + case TelnetOptions.TELOPT_TM: { + if (negotiation.server === TelnetControlSequences.DO) { + socket.emit('requestTimingMark'); + } } } }); diff --git a/backend/src/core/sockets/types/client-to-server-events.ts b/backend/src/core/sockets/types/client-to-server-events.ts index 3c92975f..c92b4b05 100644 --- a/backend/src/core/sockets/types/client-to-server-events.ts +++ b/backend/src/core/sockets/types/client-to-server-events.ts @@ -2,4 +2,5 @@ export interface ClientToServerEvents { mudConnect: () => void; mudDisconnect: () => void; mudInput: (data: string) => void; + answerTimingMark: () => void; } diff --git a/backend/src/core/sockets/types/server-to-client-events.ts b/backend/src/core/sockets/types/server-to-client-events.ts index 8093ed12..cf03513c 100644 --- a/backend/src/core/sockets/types/server-to-client-events.ts +++ b/backend/src/core/sockets/types/server-to-client-events.ts @@ -3,4 +3,5 @@ export interface ServerToClientEvents { mudDisconnected: () => void; mudConnected: () => void; setEchoMode: (showEchos: boolean) => void; + requestTimingMark: () => void; } diff --git a/backend/src/features/telnet/README.md b/backend/src/features/telnet/README.md index 0673fa7a..abf2db53 100644 --- a/backend/src/features/telnet/README.md +++ b/backend/src/features/telnet/README.md @@ -30,6 +30,7 @@ The TelnetClient class handles these negotiation commands, regardless of their o | STATUS | Full | DO | WILL | Allows for comparisson of the status of the server and the client. Allows for comparison of the negotiated options | https://github.com/unitopia-de/webmud3/issues/130 | | MSSP (Mud Server Status Protocol) | Full | DO | WILL | Allows our client to retrieve basic information about the mud, like the current player count or the server name. | https://github.com/unitopia-de/webmud3/issues/131 | | EOR (End of Record) | Full | DO | WILL | Allows for the server to signal the end of a record which is very useful to segment messages to the client. | https://github.com/unitopia-de/webmud3/issues/112 | +| TM (TIMING MARK) | Full | DO | WILL | This option is currently used to generate Ping round-trips from the server to the client. | https://github.com/unitopia-de/webmud3/issues/129 | | NAWS (Negotiate About Window Size) | Partial | WILL (+ Sub) | DO | We support this option to subnegotiate the window size. However, we send static values for the window size (80x25) and it does look like Unitopia is ignoring these values. | https://github.com/unitopia-de/webmud3/issues/108 | | CHARSET | Partial | DO / WILL (+ Sub) | WILL (+ Sub) / DO | We support this option to subnegotiate the character set with the server. However, we only accept UTF-8. If the server does not subnogitiate for UTF-8, an error will be thrown and the connection will be closed. | https://github.com/unitopia-de/webmud3/issues/111 | | LINEMODE | Partial | WILL | DO | We support the LINEMODE option. However, the server wants us to send our input buffer whenever ANY ASCII control character is entered (FORWARDMASK), which is unsupported. On the other side, we do support SOFT_TAB and EDIT MODES | https://github.com/unitopia-de/webmud3/issues/114 | @@ -43,7 +44,6 @@ The TelnetClient class handles these negotiation commands, regardless of their o | AUTH | Unsupported | WONT | DO | We dont support any authentication options for now. | | | MXP (Mud Extention Protocol) | Unsupported | WONT | DO | We dont support the MXP option for now, but we want to support this option in the future. | | | GMCP (Generic Mud Communication Protocol) | Unsupported | WONT | DO | We dont support the GMCP option for now, but we want to support this option in the future. | | -| TIMING MARK | Unsupported | Not negotiated | Not negotiated | Unsupported. Includes dirty IAC Commands in our clean data handling. | https://github.com/unitopia-de/webmud3/issues/129 | ## How to handle a new option diff --git a/backend/src/features/telnet/telnet-client.ts b/backend/src/features/telnet/telnet-client.ts index 38b18142..a83bea18 100644 --- a/backend/src/features/telnet/telnet-client.ts +++ b/backend/src/features/telnet/telnet-client.ts @@ -206,6 +206,10 @@ export class TelnetClient extends EventEmitter { this.telnetSocket.writeSub(TelnetOptions.TELOPT_STATUS, buffer); } + public sendTimingMark(): void { + this.telnetSocket.writeWill(TelnetOptions.TELOPT_TM); + } + public disconnect(): void { logger.info(`[Telnet-Client] Disconnect`); @@ -243,6 +247,12 @@ export class TelnetClient extends EventEmitter { server: TelnetControlSequences.DO, }); + // Timing Mark is excluded here since it needs a whole round-trip to the client + // We dont want to answer directly so we wait for the client to send the timing mark + if (option === TelnetOptions.TELOPT_TM) { + return; + } + const handler = this.optionsHandler.get(option); if ( diff --git a/frontend/src/app/features/sockets/sockets.service.ts b/frontend/src/app/features/sockets/sockets.service.ts index 6d63a726..35c84be2 100644 --- a/frontend/src/app/features/sockets/sockets.service.ts +++ b/frontend/src/app/features/sockets/sockets.service.ts @@ -97,6 +97,10 @@ export class SocketsService { this.socket.on('setEchoMode', (showEchos: boolean) => { this.handleSetEchoMode(showEchos); }); + + this.socket.on('requestTimingMark', () => { + this.handleTimingMark(); + }); } public connectToMud(): void { @@ -194,6 +198,12 @@ export class SocketsService { this.onSetEchoMode.emit(showEchos); }; + + private handleTimingMark = () => { + console.info('[Sockets] Sockets-Service: Got a Timing Mark'); + + this.socket.emit('answerTimingMark'); + }; } // Temporär auskommentierte Features diff --git a/frontend/src/app/features/sockets/types/client-to-server-events.ts b/frontend/src/app/features/sockets/types/client-to-server-events.ts index 3c92975f..c92b4b05 100644 --- a/frontend/src/app/features/sockets/types/client-to-server-events.ts +++ b/frontend/src/app/features/sockets/types/client-to-server-events.ts @@ -2,4 +2,5 @@ export interface ClientToServerEvents { mudConnect: () => void; mudDisconnect: () => void; mudInput: (data: string) => void; + answerTimingMark: () => void; } diff --git a/frontend/src/app/features/sockets/types/server-to-client-events.ts b/frontend/src/app/features/sockets/types/server-to-client-events.ts index 8093ed12..cf03513c 100644 --- a/frontend/src/app/features/sockets/types/server-to-client-events.ts +++ b/frontend/src/app/features/sockets/types/server-to-client-events.ts @@ -3,4 +3,5 @@ export interface ServerToClientEvents { mudDisconnected: () => void; mudConnected: () => void; setEchoMode: (showEchos: boolean) => void; + requestTimingMark: () => void; } From fed2f44061eae015b7ce75416ac071d6a52f8420 Mon Sep 17 00:00:00 2001 From: myst Date: Sat, 23 Nov 2024 23:50:58 +0100 Subject: [PATCH 2/2] feat(backend): simplified timing mark events --- backend/src/core/sockets/socket-manager.ts | 8 +++----- .../src/core/sockets/types/client-to-server-events.ts | 1 - .../src/core/sockets/types/server-to-client-events.ts | 2 +- frontend/src/app/features/sockets/sockets.service.ts | 10 +++++----- .../features/sockets/types/client-to-server-events.ts | 1 - .../features/sockets/types/server-to-client-events.ts | 2 +- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/backend/src/core/sockets/socket-manager.ts b/backend/src/core/sockets/socket-manager.ts index d4534449..211b2ecd 100644 --- a/backend/src/core/sockets/socket-manager.ts +++ b/backend/src/core/sockets/socket-manager.ts @@ -108,10 +108,6 @@ export class SocketManager extends Server< }, Environment.getInstance().socketTimeout); }); - socket.on('answerTimingMark', () => { - this.mudConnections[socket.id].telnet?.sendTimingMark(); - }); - socket.on('mudInput', (data: string) => { const inputEcho = this.mudConnections[socket.id].echo; @@ -218,7 +214,9 @@ export class SocketManager extends Server< case TelnetOptions.TELOPT_TM: { if (negotiation.server === TelnetControlSequences.DO) { - socket.emit('requestTimingMark'); + socket.emit('requestTimingMark', () => { + this.mudConnections[socket.id].telnet?.sendTimingMark(); + }); } } } diff --git a/backend/src/core/sockets/types/client-to-server-events.ts b/backend/src/core/sockets/types/client-to-server-events.ts index c92b4b05..3c92975f 100644 --- a/backend/src/core/sockets/types/client-to-server-events.ts +++ b/backend/src/core/sockets/types/client-to-server-events.ts @@ -2,5 +2,4 @@ export interface ClientToServerEvents { mudConnect: () => void; mudDisconnect: () => void; mudInput: (data: string) => void; - answerTimingMark: () => void; } diff --git a/backend/src/core/sockets/types/server-to-client-events.ts b/backend/src/core/sockets/types/server-to-client-events.ts index cf03513c..8348e731 100644 --- a/backend/src/core/sockets/types/server-to-client-events.ts +++ b/backend/src/core/sockets/types/server-to-client-events.ts @@ -3,5 +3,5 @@ export interface ServerToClientEvents { mudDisconnected: () => void; mudConnected: () => void; setEchoMode: (showEchos: boolean) => void; - requestTimingMark: () => void; + requestTimingMark: (callback: () => void) => void; } diff --git a/frontend/src/app/features/sockets/sockets.service.ts b/frontend/src/app/features/sockets/sockets.service.ts index 35c84be2..06fb8677 100644 --- a/frontend/src/app/features/sockets/sockets.service.ts +++ b/frontend/src/app/features/sockets/sockets.service.ts @@ -98,8 +98,8 @@ export class SocketsService { this.handleSetEchoMode(showEchos); }); - this.socket.on('requestTimingMark', () => { - this.handleTimingMark(); + this.socket.on('requestTimingMark', (callback: () => void) => { + this.handleTimingMark(callback); }); } @@ -199,10 +199,10 @@ export class SocketsService { this.onSetEchoMode.emit(showEchos); }; - private handleTimingMark = () => { - console.info('[Sockets] Sockets-Service: Got a Timing Mark'); + private handleTimingMark = (callback: () => void) => { + console.info('[Sockets] Sockets-Service: Got and answer a Timing Mark'); - this.socket.emit('answerTimingMark'); + callback(); }; } diff --git a/frontend/src/app/features/sockets/types/client-to-server-events.ts b/frontend/src/app/features/sockets/types/client-to-server-events.ts index c92b4b05..3c92975f 100644 --- a/frontend/src/app/features/sockets/types/client-to-server-events.ts +++ b/frontend/src/app/features/sockets/types/client-to-server-events.ts @@ -2,5 +2,4 @@ export interface ClientToServerEvents { mudConnect: () => void; mudDisconnect: () => void; mudInput: (data: string) => void; - answerTimingMark: () => void; } diff --git a/frontend/src/app/features/sockets/types/server-to-client-events.ts b/frontend/src/app/features/sockets/types/server-to-client-events.ts index cf03513c..8348e731 100644 --- a/frontend/src/app/features/sockets/types/server-to-client-events.ts +++ b/frontend/src/app/features/sockets/types/server-to-client-events.ts @@ -3,5 +3,5 @@ export interface ServerToClientEvents { mudDisconnected: () => void; mudConnected: () => void; setEchoMode: (showEchos: boolean) => void; - requestTimingMark: () => void; + requestTimingMark: (callback: () => void) => void; }