diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 441ef5d..3ac1ed5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,8 +13,17 @@ Some examples of this are: To see the current list of supported types, see [CI.yml](./.github/workflows/ci.yml#L13)'s 'Lint PR Title' task. -# Tests +# Build +To build and use this extension locally, run the following commands: + ``` +cd webview-ui +npm install +``` +Go to the Run and Debug panel (Ctrl-Shift-D) and select "Run Extension". This will automatically build and run the extension. +You can now use the extension as normal (in the new VS Code instance) by attaching to Minecraft to debug. + +# Tests Create tests using vitest. Follow the convention `sourcefilename.test.ts` when adding new files. diff --git a/src/breakpoints-legacy.ts b/src/breakpoints-legacy.ts index 0756fe0..59fb1ef 100644 --- a/src/breakpoints-legacy.ts +++ b/src/breakpoints-legacy.ts @@ -4,6 +4,7 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import { IBreakpointsHandler } from './ibreakpoints-handler'; import { SourceMaps } from './source-maps'; import { IDebuggeeMessageSender } from './debuggee-message-sender'; +import { BreakpointsMessage, OutgoingEventType } from './protocol-events'; import * as path from 'path'; // respond to a setBreakPointsRequest from session, deprecated. @@ -67,12 +68,10 @@ export class BreakpointsLegacy implements IBreakpointsHandler { // send full set of breakpoints for each generated file, a message per file for (const [generatedRemoteLocalPath, generatedBreakpoints] of generatedBreakpointsMap) { - const envelope = { - type: 'breakpoints', - breakpoints: { - path: generatedRemoteLocalPath, - breakpoints: generatedBreakpoints.length ? generatedBreakpoints : undefined, - }, + const envelope: BreakpointsMessage = { + type: OutgoingEventType.Breakpoints, + path: generatedRemoteLocalPath, + breakpoints: generatedBreakpoints.length ? generatedBreakpoints : undefined, }; this._messageSender.sendDebuggeeMessage(envelope); } diff --git a/src/debuggee-message-sender.ts b/src/debuggee-message-sender.ts index 73ea598..0370708 100644 --- a/src/debuggee-message-sender.ts +++ b/src/debuggee-message-sender.ts @@ -1,9 +1,10 @@ // Copyright (C) Microsoft Corporation. All rights reserved. import { DebugProtocol } from '@vscode/debugprotocol'; +import { OutgoingDebuggeeMessage } from './protocol-events'; // Interface for sending debugger messages and requests to MC export interface IDebuggeeMessageSender { - sendDebuggeeMessage(envelope: unknown): void; + sendDebuggeeMessage(envelope: OutgoingDebuggeeMessage): void; sendDebugeeRequestAsync(response: DebugProtocol.Response, args: unknown): Promise; } diff --git a/src/protocol-events.ts b/src/protocol-events.ts new file mode 100644 index 0000000..8ca3fef --- /dev/null +++ b/src/protocol-events.ts @@ -0,0 +1,257 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. + +import { LogLevel } from '@vscode/debugadapter/lib/logger'; +import { DebugProtocol } from '@vscode/debugprotocol'; +import { StatMessageModel } from './stats/stats-provider'; + +// protocol version history +// 1 - initial version +// 2 - add targetModuleUuid to protocol event +// 3 - add array of plugins and target module ids to incoming protocol event +// 4 - mc can require a passcode to connect +// 5 - debugger can take mc script profiler captures +// 6 - breakpoints as request, MC can reject +// 7 - support for debugger requests, MC can reject or respond with args +// 8 - New serialization tech (use Cereal) + +export enum ProtocolVersion { + _Unknown = 0, + Initial = 1, + SupportTargetModuleUuid = 2, + SupportTargetSelection = 3, + SupportPasscode = 4, + SupportProfilerCaptures = 5, + SupportBreakpointsAsRequest = 6, + SupportDebuggerRequests = 7, + SupportCerealSerialization = 8, +} + +export const DEBUGGER_PROTOCOL_VERSION = ProtocolVersion.SupportCerealSerialization; + +// ------------------------------------------------------------------------- +// Interfaces for event message payloads (received from the debugee) +// ------------------------------------------------------------------------- +export enum IncomingEventType { + Stopped = 'StoppedEvent', + Thread = 'ThreadEvent', + Print = 'PrintEvent', + Notification = 'NotificationEvent', + Protocol = 'ProtocolEvent', + Stat2 = 'StatEvent2', + Schema = 'SchemaEvent', + ProfilerCapture = 'ProfilerCapture', + DebuggeeResponse = 'debuggee-response', +} + +export interface PluginDetails { + name: string; + module_uuid: string; +} + +export interface ProtocolCapabilities { + type: IncomingEventType.Protocol; + version: number; + plugins: PluginDetails[]; + require_passcode?: boolean; +} + +export interface ProfilerCapture { + type: IncomingEventType.ProfilerCapture; + capture_base_path: string; + capture_data: string; +} + +export interface StoppedEventMessage { + type: IncomingEventType.Stopped; + reason: string; + thread: number; +} + +export interface ThreadEventMessage { + type: IncomingEventType.Thread; + reason: string; + thread: number; +} + +export interface PrintEventMessage { + type: IncomingEventType.Print; + message: string; + logLevel: LogLevel; +} + +export interface NotificationEventMessage { + type: IncomingEventType.Notification; + message: string; + logLevel: LogLevel; +} + +export interface DebuggeeResponseEnvelope { + type: IncomingEventType.DebuggeeResponse; + request_seq: number; + args?: unknown; + success?: boolean; + response_message?: string; +} + +export type StatEventMessage = StatMessageModel & { + type: IncomingEventType.Stat2; +}; + +export type IncomingDebuggeeMessage = + | ProtocolCapabilities + | ProfilerCapture + | StoppedEventMessage + | ThreadEventMessage + | PrintEventMessage + | NotificationEventMessage + | StatEventMessage + | DebuggeeResponseEnvelope; + + +// ------------------------------------------------------------------------- +// Interfaces for outbound message payloads (sent to the debugee) +// ------------------------------------------------------------------------- +export enum OutgoingEventType { + Protocol = 'protocol', + MinecraftCommand = 'minecraftCommand', + StartProfiler = 'startProfiler', + StopProfiler = 'stopProfiler', + StopOnException = 'stopOnException', + Resume = 'resume', + Request = 'request', + Breakpoints = 'breakpoints', + DebuggerRequest = 'debugger-request' +} + +export interface ProtocolResponse { + type: OutgoingEventType.Protocol; + version: number; + target_module_uuid?: string; + passcode?: string; +} + +export interface MinecraftCommandLegacyMessage { + type: OutgoingEventType.MinecraftCommand; + command: { command: string; dimension_type: string }; +} + +export interface MinecraftCommandMessage { + type: OutgoingEventType.MinecraftCommand; + command: string; + dimension_type: string; +} + +export interface StartProfilerLegacyMessage { + type: OutgoingEventType.StartProfiler; + profiler: { target_module_uuid?: string }; +} + +export interface StartProfilerMessage { + type: OutgoingEventType.StartProfiler; + target_module_uuid?: string; +} + +export interface StopProfilerLegacyMessage { + type: OutgoingEventType.StopProfiler; + profiler: { captures_path: string; target_module_uuid?: string }; +} + +export interface StopProfilerMessage { + type: OutgoingEventType.StopProfiler; + captures_path: string; + target_module_uuid?: string; +} + +export interface StopOnExceptionMessage { + type: OutgoingEventType.StopOnException; + stopOnException: boolean; +} + +export interface ResumeMessage { + type: OutgoingEventType.Resume; +} + +export interface RequestLegacyMessage { + type: OutgoingEventType.Request; + request: { request_seq: number; command: string; args: unknown }; +} + +export interface RequestMessage { + type: OutgoingEventType.Request; + request_seq: number; + command: string; + args: unknown; +} + +export interface BreakpointsLegacyMessage { + type: OutgoingEventType.Breakpoints; + breakpoints: { path: string; breakpoints: DebugProtocol.SourceBreakpoint[] | undefined }; +} + +export interface BreakpointsMessage { + type: OutgoingEventType.Breakpoints; + path: string; + breakpoints: DebugProtocol.SourceBreakpoint[] | undefined; +} + +export interface DebuggerRequestLegacyEnvelope { + type: OutgoingEventType.DebuggerRequest; + request: { + request_seq: number; + request: string; + args?: unknown; + }; +} + +export interface DebuggerRequestEnvelope { + type: OutgoingEventType.DebuggerRequest; + request_seq: number; + request: string; + args?: unknown; +} + +export type OutgoingDebuggeeMessage = + | ProtocolResponse + | MinecraftCommandLegacyMessage + | MinecraftCommandMessage + | StartProfilerLegacyMessage + | StartProfilerMessage + | StopProfilerLegacyMessage + | StopProfilerMessage + | StopOnExceptionMessage + | ResumeMessage + | RequestLegacyMessage + | RequestMessage + | BreakpointsLegacyMessage + | BreakpointsMessage + | DebuggerRequestLegacyEnvelope + | DebuggerRequestEnvelope; + + + +// ------------------------------------------------------------------------- +// Registry that maps event type name strings to handler callbacks +// ------------------------------------------------------------------------- + +type DebuggeeEventHandler = ( + event: Extract, +) => void; + +export class DebuggeeEventRegistry { + private readonly _handlers = new Map void>(); + + public register(eventType: T, handler: DebuggeeEventHandler): void { + this._handlers.set(eventType, event => { + handler(event as Extract); + }); + } + + public dispatch(eventMessage: IncomingDebuggeeMessage): boolean { + const handler = this._handlers.get(eventMessage.type); + if (handler) { + handler(eventMessage); + return true; + } + return false; + } +} diff --git a/src/requests/debugger-request-schema.ts b/src/requests/debugger-request-schema.ts index 5b0a73e..81aa67b 100644 --- a/src/requests/debugger-request-schema.ts +++ b/src/requests/debugger-request-schema.ts @@ -4,23 +4,4 @@ export interface DebuggerRequestArguments { request: string; args?: unknown; -} - -// Sent from the debug session to the debuggee (MC) -export interface DebuggerRequestEnvelope { - type: 'debugger-request'; - request: { - request_seq: number; - request: string; - args?: unknown; - }; -} - -// Received from the debuggee (MC) in response to a DebuggerRequestEnvelope -export interface DebuggeeResponseEnvelope { - type: 'debuggee-response'; - request_seq: number; - args?: unknown; - success?: boolean; - response_message?: string; -} +} \ No newline at end of file diff --git a/src/requests/request-manager.test.ts b/src/requests/request-manager.test.ts index 0bfd83c..5a6736c 100644 --- a/src/requests/request-manager.test.ts +++ b/src/requests/request-manager.test.ts @@ -4,6 +4,7 @@ import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest'; import type { DebugProtocol } from '@vscode/debugprotocol'; import { RequestManager } from './request-manager'; import type { IDebuggeeMessageSender } from '../debuggee-message-sender'; +import { IncomingEventType } from '../protocol-events'; describe('RequestManager', () => { let manager: RequestManager; @@ -21,13 +22,13 @@ describe('RequestManager', () => { manager = new RequestManager(mockSender); const response = { request_seq: 42 } as DebugProtocol.Response; - const promise = manager.sendDebuggerRequest(response, { + const promise = manager.sendDebuggerRequest(0, response, { request: 'test-debugger-request', args: { foo: 'bar' }, }); const handled = manager.handleDebuggeeResponse({ - type: 'debuggee-response', + type: IncomingEventType.DebuggeeResponse, request_seq: 42, success: true, args: { ok: true }, @@ -44,7 +45,7 @@ describe('RequestManager', () => { }); expect(handled).toBe(true); await expect(promise).resolves.toEqual({ - type: 'debuggee-response', + type: IncomingEventType.DebuggeeResponse, request_seq: 42, success: true, args: { ok: true }, @@ -56,7 +57,7 @@ describe('RequestManager', () => { manager = new RequestManager(mockSender); const response = { request_seq: 123 } as DebugProtocol.Response; - const promise = manager.sendDebuggerRequest(response, { + const promise = manager.sendDebuggerRequest(0, response, { request: 'test-debugger-request', }); const rejection = expect(promise).rejects.toThrow( @@ -73,12 +74,12 @@ describe('RequestManager', () => { it('should reject request on failed response', async () => { manager = new RequestManager(mockSender); const response = { request_seq: 7 } as DebugProtocol.Response; - const promise = manager.sendDebuggerRequest(response, { + const promise = manager.sendDebuggerRequest(0, response, { request: 'test-debugger-request', }); manager.handleDebuggeeResponse({ - type: 'debuggee-response', + type: IncomingEventType.DebuggeeResponse, request_seq: 7, success: false, response_message: 'Denied', @@ -91,7 +92,7 @@ describe('RequestManager', () => { manager = new RequestManager(mockSender); const handled = manager.handleDebuggeeResponse({ - type: 'debuggee-response', + type: IncomingEventType.DebuggeeResponse, request_seq: -1, success: true, }); @@ -106,8 +107,8 @@ describe('RequestManager', () => { const responseA = { request_seq: 1 } as DebugProtocol.Response; const responseB = { request_seq: 2 } as DebugProtocol.Response; - const promiseA = manager.sendDebuggerRequest(responseA, { request: 'A' }); - const promiseB = manager.sendDebuggerRequest(responseB, { request: 'B' }); + const promiseA = manager.sendDebuggerRequest(0, responseA, { request: 'A' }); + const promiseB = manager.sendDebuggerRequest(0, responseB, { request: 'B' }); manager.rejectPendingRequests('Disconnected'); diff --git a/src/requests/request-manager.ts b/src/requests/request-manager.ts index 7826421..9d72ddc 100644 --- a/src/requests/request-manager.ts +++ b/src/requests/request-manager.ts @@ -1,8 +1,9 @@ // Copyright (C) Microsoft Corporation. All rights reserved. import { DebugProtocol } from '@vscode/debugprotocol'; -import { DebuggerRequestArguments, DebuggerRequestEnvelope, DebuggeeResponseEnvelope } from './debugger-request-schema'; +import { DebuggerRequestArguments} from './debugger-request-schema'; import { IDebuggeeMessageSender } from '../debuggee-message-sender'; +import { DebuggeeResponseEnvelope, OutgoingEventType, ProtocolVersion } from '../protocol-events'; interface PendingDebuggerRequest { resolve: (value: DebuggeeResponseEnvelope) => void; @@ -20,6 +21,7 @@ export class RequestManager { } public sendDebuggerRequest( + clientProtocolVersion: number, response: DebugProtocol.Response, debuggerRequestArgs: DebuggerRequestArguments, timeoutMs: number = this._defaultDebuggerRequestTimeoutMs, @@ -45,16 +47,23 @@ export class RequestManager { }); // Create an envelope to hold the request, and send it to the debuggee - const envelope: DebuggerRequestEnvelope = { - type: 'debugger-request', - request: { + if (clientProtocolVersion >= ProtocolVersion.SupportCerealSerialization) { + this._sender.sendDebuggeeMessage({ + type: OutgoingEventType.DebuggerRequest, request_seq: seq, request, args, - }, - }; - - this._sender.sendDebuggeeMessage(envelope); + }); + } else { + this._sender.sendDebuggeeMessage({ + type: OutgoingEventType.DebuggerRequest, + request: { + request_seq: seq, + request, + args, + }, + }); + } }); } diff --git a/src/session.ts b/src/session.ts index eeaee11..43fd404 100644 --- a/src/session.ts +++ b/src/session.ts @@ -42,10 +42,28 @@ import { IBreakpointsHandler } from './ibreakpoints-handler'; import { injectSourceMapIntoProfilerCapture } from './profiler-utils'; import { isUUID } from './utils'; import { MessageStreamParser } from './message-stream-parser'; -import { DebuggerRequestArguments, DebuggeeResponseEnvelope } from './requests/debugger-request-schema'; -import { RequestManager } from './requests/request-manager'; +import { + DebuggeeEventRegistry, + DEBUGGER_PROTOCOL_VERSION, + IncomingEventType, + NotificationEventMessage, + OutgoingDebuggeeMessage, + OutgoingEventType, + PluginDetails, + PrintEventMessage, + ProfilerCapture, + ProtocolCapabilities, + ProtocolVersion, + RequestMessage, + StoppedEventMessage, + ThreadEventMessage, + DebuggeeResponseEnvelope, + RequestLegacyMessage +} from './protocol-events'; import { SourceMaps } from './source-maps'; import { StatMessageModel, StatsProvider } from './stats/stats-provider'; +import { RequestManager } from './requests/request-manager'; +import { DebuggerRequestArguments } from './requests/debugger-request-schema'; interface PendingResponse { onSuccess?: (result: any) => void; @@ -57,24 +75,6 @@ export interface ModuleMapping { [moduleName: string]: string; } -interface PluginDetails { - name: string; - module_uuid: string; -} - -interface ProtocolCapabilities { - type: string; - version: number; - plugins: PluginDetails[]; - require_passcode?: boolean; -} - -interface ProfilerCapture { - type: string; - capture_base_path: string; - capture_data: string; -} - // Interface for specific launch arguments. // See package.json for schema. interface IAttachRequestArguments extends DebugProtocol.AttachRequestArguments { @@ -110,25 +110,6 @@ interface DebuggerStackFrame { column: number; } -// protocol version history -// 1 - initial version -// 2 - add targetModuleUuid to protocol event -// 3 - add array of plugins and target module ids to incoming protocol event -// 4 - mc can require a passcode to connect -// 5 - debugger can take mc script profiler captures -// 6 - breakpoints as request, MC can reject -// 7 - support for debugger requests, MC can reject or respond with args -enum ProtocolVersion { - _Unknown = 0, - Initial = 1, - SupportTargetModuleUuid = 2, - SupportTargetSelection = 3, - SupportPasscode = 4, - SupportProfilerCaptures = 5, - SupportBreakpointsAsRequest = 6, - SupportDebuggerRequests = 7, -} - // capabilites based on protocol version export interface MinecraftCapabilities { supportsCommands: boolean; @@ -140,10 +121,8 @@ export interface MinecraftCapabilities { // The Debug Adapter for 'minecraft-js' // export class Session extends DebugSession implements IDebuggeeMessageSender { - private readonly _debuggerProtocolVersion = ProtocolVersion.SupportDebuggerRequests; private readonly _connectionRetryAttempts = 3; private readonly _connectionRetryWaitMs = 500; - private _debugeeServer?: Server; // when listening for incoming connections private _connectionSocket?: Socket; private _connected = false; @@ -168,6 +147,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { supportsDebuggerRequests: false, }; private _passcode?: string; + private _eventRegistry: DebuggeeEventRegistry; // external communication private _homeViewProvider: HomeViewProvider; @@ -185,12 +165,45 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { this.setDebuggerLinesStartAt1(true); this.setDebuggerColumnsStartAt1(true); + this._eventRegistry = new DebuggeeEventRegistry(); + this.registerServerEvents(); + this._eventEmitter.on('run-minecraft-command', this.onRunMinecraftCommand.bind(this)); this._eventEmitter.on('start-profiler', this.onStartProfiler.bind(this)); this._eventEmitter.on('stop-profiler', this.onStopProfiler.bind(this)); this._eventEmitter.on('request-debugger-status', this.onRequestDebuggerStatus.bind(this)); } + // Use this to register new events that are handled from the debugee (Minecraft) + // For example you want to send new arbitary data to the debugger that doesn't fit into the existing event types (such as a live stat) + // then you can create a new event type in protocol-events.ts, have Minecraft send that event with the new data, and then register a handler + // for that event here to handle the incoming data and do something with it (e.g. update the home view, send a notification, etc). + private registerServerEvents() { + this._eventRegistry.register(IncomingEventType.Stopped, (msg: StoppedEventMessage) => { + this.trackThreadChanges(msg.reason, msg.thread); + this.sendEvent(new StoppedEvent(msg.reason, msg.thread)); + }); + this._eventRegistry.register(IncomingEventType.Thread, (msg: ThreadEventMessage) => { + this.trackThreadChanges(msg.reason, msg.thread); + this.sendEvent(new ThreadEvent(msg.reason, msg.thread)); + }); + this._eventRegistry.register(IncomingEventType.Print, (msg: PrintEventMessage) => { + this.handlePrintEvent(msg.message, msg.logLevel); + }); + this._eventRegistry.register(IncomingEventType.Notification, (msg: NotificationEventMessage) => { + this.showNotification(msg.message, msg.logLevel); + }); + this._eventRegistry.register(IncomingEventType.Protocol, (msg: ProtocolCapabilities) => { + this.handleProtocolEvent(msg); + }); + this._eventRegistry.register(IncomingEventType.Stat2, (msg: StatMessageModel) => { + this._statsProvider.setStats(msg); + }); + this._eventRegistry.register(IncomingEventType.ProfilerCapture, (msg: ProfilerCapture) => { + this.handleProfilerCapture(msg); + }); + } + public dispose(): void { this._eventEmitter.removeAllListeners('run-minecraft-command'); this._eventEmitter.removeAllListeners('start-profiler'); @@ -208,15 +221,16 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { // ------------------------------------------------------------------------ private onRunMinecraftCommand(command: string): void { - if (this._clientProtocolVersion < ProtocolVersion.SupportProfilerCaptures) { + if (this._clientProtocolVersion < ProtocolVersion.SupportProfilerCaptures || + this._clientProtocolVersion >= ProtocolVersion.SupportCerealSerialization) { this.sendDebuggeeMessage({ - type: 'minecraftCommand', + type: OutgoingEventType.MinecraftCommand, command: command, dimension_type: 'overworld', }); } else { this.sendDebuggeeMessage({ - type: 'minecraftCommand', + type: OutgoingEventType.MinecraftCommand, command: { command: command, dimension_type: 'overworld', @@ -226,22 +240,38 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { } private onStartProfiler(): void { - this.sendDebuggeeMessage({ - type: 'startProfiler', - profiler: { + if (this._clientProtocolVersion >= ProtocolVersion.SupportCerealSerialization) { + this.sendDebuggeeMessage({ + type: OutgoingEventType.StartProfiler, target_module_uuid: this._targetModuleUuid, - }, - }); + }); + } + else { + this.sendDebuggeeMessage({ + type: OutgoingEventType.StartProfiler, + profiler: { + target_module_uuid: this._targetModuleUuid, + }, + }); + } } private onStopProfiler(capturesBasePath: string): void { - this.sendDebuggeeMessage({ - type: 'stopProfiler', - profiler: { + if (this._clientProtocolVersion >= ProtocolVersion.SupportCerealSerialization) { + this.sendDebuggeeMessage({ + type: OutgoingEventType.StopProfiler, captures_path: capturesBasePath, target_module_uuid: this._targetModuleUuid, - }, - }); + }); + } else { + this.sendDebuggeeMessage({ + type: OutgoingEventType.StopProfiler, + profiler: { + captures_path: capturesBasePath, + target_module_uuid: this._targetModuleUuid, + }, + }); + } } private writeProfilerCaptureToFile( @@ -437,7 +467,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { args: DebugProtocol.SetExceptionBreakpointsArguments, ): void { this.sendDebuggeeMessage({ - type: 'stopOnException', + type: OutgoingEventType.StopOnException, stopOnException: args.filters.length > 0, // there's only 1 type for now so no need to look at which one it is }); @@ -446,7 +476,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse): void { this.sendDebuggeeMessage({ - type: 'resume', + type: OutgoingEventType.Resume, }); this.sendResponse(response); @@ -661,6 +691,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { try { const result = await this._requestManager?.sendDebuggerRequest( + this._clientProtocolVersion, response, args as DebuggerRequestArguments, ); @@ -763,7 +794,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { // respond with protocol version and chosen debugee target this.sendDebuggeeMessage({ - type: 'protocol', + type: OutgoingEventType.Protocol, version: protocolVersion, target_module_uuid: targetModuleUuid, passcode: passcode, @@ -880,19 +911,30 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { this.sendDebuggeeMessage(this.makeRequestPayload(requestSeq, response.command, args)); } - private makeRequestPayload(requestSeq: number, responseCommand: string, args: any) { - const envelope = { - type: 'request', - request: { + private makeRequestPayload(requestSeq: number, responseCommand: string, args: unknown): RequestMessage | RequestLegacyMessage { + if (this._clientProtocolVersion >= ProtocolVersion.SupportCerealSerialization) { + const envelope: RequestMessage = { + type: OutgoingEventType.Request, request_seq: requestSeq, command: responseCommand, args, - }, - }; - return envelope; + }; + return envelope; + } + else { + const envelope: RequestLegacyMessage = { + type: OutgoingEventType.Request, + request: { + request_seq: requestSeq, + command: responseCommand, + args, + }, + }; + return envelope; + } } - public sendDebuggeeMessage(envelope: unknown): void { + public sendDebuggeeMessage(envelope: OutgoingDebuggeeMessage): void { if (!this._connectionSocket) { return; } @@ -915,7 +957,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { private receiveDebugeeMessage(envelope: any) { if (envelope.type === 'event') { this.handleDebugeeEvent(envelope.event); - } else if (envelope.type === 'debuggee-response') { + } else if (envelope.type === IncomingEventType.DebuggeeResponse) { if (!this._minecraftCapabilities.supportsDebuggerRequests) { this.log( 'Received debuggee-response from a Minecraft instance that should not support it.', @@ -924,31 +966,18 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { return; } - this._requestManager?.handleDebuggeeResponse(envelope as DebuggeeResponseEnvelope); + this._requestManager?.handleDebuggeeResponse(envelope.event as DebuggeeResponseEnvelope); } else if (envelope.type === 'response') { this.handleDebugeeResponse(envelope); } + else { + this.log(`Debugee message error: Unknown message type: ${envelope?.type ?? "NO TYPE"}`, LogLevel.Error); + } } // Debugee (MC) has sent an event. private handleDebugeeEvent(eventMessage: any) { - if (eventMessage.type === 'StoppedEvent') { - this.trackThreadChanges(eventMessage.reason, eventMessage.thread); - this.sendEvent(new StoppedEvent(eventMessage.reason, eventMessage.thread)); - } else if (eventMessage.type === 'ThreadEvent') { - this.trackThreadChanges(eventMessage.reason, eventMessage.thread); - this.sendEvent(new ThreadEvent(eventMessage.reason, eventMessage.thread)); - } else if (eventMessage.type === 'PrintEvent') { - this.handlePrintEvent(eventMessage.message, eventMessage.logLevel); - } else if (eventMessage.type === 'NotificationEvent') { - this.showNotification(eventMessage.message, eventMessage.logLevel); - } else if (eventMessage.type === 'ProtocolEvent') { - this.handleProtocolEvent(eventMessage as ProtocolCapabilities); - } else if (eventMessage.type === 'StatEvent2') { - this._statsProvider.setStats(eventMessage as StatMessageModel); - } else if (eventMessage.type === 'ProfilerCapture') { - this.handleProfilerCapture(eventMessage as ProfilerCapture); - } + this._eventRegistry.dispatch(eventMessage); } private async handlePrintEvent(message: string, logLevel: LogLevel) { @@ -1017,7 +1046,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { // handle protocol capabilities here... // can fail connection on errors // - if (this._debuggerProtocolVersion < protocolCapabilities.version) { + if (DEBUGGER_PROTOCOL_VERSION < protocolCapabilities.version) { this.terminateSession( `protocol unsupported. Upgrade Debugger Extension. Protocol Version: ${protocolCapabilities.version} is not supported by the current version of the Debugger.`, LogLevel.Error, diff --git a/webview-ui/src/diagnostics_panel/utilities/useDebuggerRequests.ts b/webview-ui/src/diagnostics_panel/utilities/useDebuggerRequests.ts index 6efdada..ba21228 100644 --- a/webview-ui/src/diagnostics_panel/utilities/useDebuggerRequests.ts +++ b/webview-ui/src/diagnostics_panel/utilities/useDebuggerRequests.ts @@ -1,7 +1,7 @@ // Copyright (C) Microsoft Corporation. All rights reserved. import { useEffect, useState } from 'react'; -import type { DebuggeeResponseEnvelope } from '../../../../src/requests/debugger-request-schema'; +import type { DebuggeeResponseEnvelope } from '../../../../src/protocol-events'; import { vscode } from './vscode'; // Result payload posted back from the extension host after a debugger request completes.