From 3eda824868e4641723dcb8d286b4193b13c2a7fa Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Fri, 27 Mar 2026 14:57:47 -0700 Subject: [PATCH 01/16] wip --- src/panels/minecraft-diagnostics.ts | 6 + src/session.ts | 2 + src/stats/stats-provider.ts | 29 +++ webview-ui/src/diagnostics_panel/App.tsx | 114 +++++++++--- .../diagnostics_panel/DiagnosticsSchema.ts | 29 +++ .../src/diagnostics_panel/DynamicTab.tsx | 166 ++++++++++++++++++ webview-ui/src/diagnostics_panel/index.tsx | 2 +- .../diagnostics_panel/prefabs/TabPrefab.ts | 2 - 8 files changed, 319 insertions(+), 31 deletions(-) create mode 100644 webview-ui/src/diagnostics_panel/DiagnosticsSchema.ts create mode 100644 webview-ui/src/diagnostics_panel/DynamicTab.tsx diff --git a/src/panels/minecraft-diagnostics.ts b/src/panels/minecraft-diagnostics.ts index b535c058..bce18d0d 100644 --- a/src/panels/minecraft-diagnostics.ts +++ b/src/panels/minecraft-diagnostics.ts @@ -96,6 +96,12 @@ export class MinecraftDiagnosticsPanel { onNotification: (message: string) => { window.showInformationMessage(message); }, + onSchemaReceived: (schema) => { + this._panel.webview.postMessage({ + type: 'diagnostics-schema', + schema, + }); + }, }; this._statsTracker.addStatListener(this._statsCallback); diff --git a/src/session.ts b/src/session.ts index 3b2059a1..3a6ce906 100644 --- a/src/session.ts +++ b/src/session.ts @@ -894,6 +894,8 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { this.handleProtocolEvent(eventMessage as ProtocolCapabilities); } else if (eventMessage.type === 'StatEvent2') { this._statsProvider.setStats(eventMessage as StatMessageModel); + } else if (eventMessage.type === 'SchemaEvent') { + this._statsProvider.setSchema(eventMessage.descriptors); } else if (eventMessage.type === 'ProfilerCapture') { this.handleProfilerCapture(eventMessage as ProfilerCapture); } diff --git a/src/stats/stats-provider.ts b/src/stats/stats-provider.ts index 48c53f80..0b8c664d 100644 --- a/src/stats/stats-provider.ts +++ b/src/stats/stats-provider.ts @@ -26,12 +26,35 @@ export interface StatMessageModel { stats: StatDataModel[]; } +// Mirrors ScriptDiagnosticsDescriptor from C++. Sent once on connect via SchemaEvent. +export interface DiagnosticsTabDescriptor { + name: string; + stat_group_id: string; + data_source: 'server' | 'client' | 'server_script'; + display_type: 'line_chart' | 'stacked_line_chart' | 'stacked_bar_chart' | 'table' | 'multi_column_table' | 'dynamic_properties_table'; + title?: string; + y_label?: string; + tick_range?: number; + value_scalar?: number; + target_value?: number; + key_label?: string; + value_labels?: string[]; + statistic_id?: string; + statistic_ids?: string[]; +} + +export interface SchemaMessageModel { + type: 'SchemaEvent'; + descriptors: DiagnosticsTabDescriptor[]; +} + export interface StatsListener { onStatUpdated?: (stat: StatData) => void; onSpeedUpdated?: (speed: number) => void; onPauseUpdated?: (paused: boolean) => void; onStopped?: () => void; onNotification?: (message: string) => void; + onSchemaReceived?: (schema: DiagnosticsTabDescriptor[]) => void; } export class StatsProvider { @@ -47,6 +70,12 @@ export class StatsProvider { } } + public setSchema(schema: DiagnosticsTabDescriptor[]): void { + this._statListeners.forEach((listener: StatsListener) => { + listener.onSchemaReceived?.(schema); + }); + } + public start(): void { throw new Error('Method not implemented.'); } diff --git a/webview-ui/src/diagnostics_panel/App.tsx b/webview-ui/src/diagnostics_panel/App.tsx index 51f6d232..d41e66fc 100644 --- a/webview-ui/src/diagnostics_panel/App.tsx +++ b/webview-ui/src/diagnostics_panel/App.tsx @@ -1,6 +1,6 @@ // Copyright (C) Microsoft Corporation. All rights reserved. -import { VSCodePanelTab, VSCodePanelView, VSCodePanels } from '@vscode/webview-ui-toolkit/react'; +import { VSCodeDropdown, VSCodeOption, VSCodePanelTab, VSCodePanelView, VSCodePanels } from '@vscode/webview-ui-toolkit/react'; import { StatGroupSelectionBox } from './controls/StatGroupSelectionBox'; import { useCallback, useEffect, useState } from 'react'; import { StatisticType, YAxisStyle, YAxisType, createStatResolver } from './StatisticResolver'; @@ -14,6 +14,8 @@ import { Icons } from './Icons'; import './App.css'; import tabPrefabs from './prefabs'; import { TabPrefabDataSource } from './prefabs/TabPrefab'; +import { DiagnosticsTabDescriptor } from './DiagnosticsSchema'; +import DynamicTab from './DynamicTab'; declare global { interface Window { @@ -53,6 +55,10 @@ function App() { const [currentTab, setCurrentTab] = useState(); const [paused, setPaused] = useState(true); const [speed, setSpeed] = useState(''); + // Dynamic schema received from the game on connect. Empty = use static prefab fallback. + const [schema, setSchema] = useState([]); + // Index of the selected tab in the dynamic dropdown + const [selectedSchemaIndex, setSelectedSchemaIndex] = useState(0); const handlePluginSelection = useCallback((pluginSelectionId: string) => { setSelectedPlugin(() => pluginSelectionId); @@ -69,6 +75,11 @@ function App() { } }, []); + const handleSchemaTabChange = useCallback((e: Event | React.FormEvent): void => { + const target = e.target as HTMLSelectElement; + setSelectedSchemaIndex(target.selectedIndex); + }, []); + useEffect(() => { const handleMessage = (event: MessageEvent) => { const message = event.data; @@ -76,6 +87,9 @@ function App() { setSpeed(`${message.speed}hz`); } else if (message.type === 'pause-updated') { setPaused(message.paused); + } else if (message.type === 'diagnostics-schema') { + setSchema(message.schema as DiagnosticsTabDescriptor[]); + setSelectedSchemaIndex(0); } }; window.addEventListener('message', handleMessage); @@ -84,6 +98,9 @@ function App() { }; }, []); + const usingDynamicSchema = schema.length > 0; + const activeDescriptor = usingDynamicSchema ? schema[selectedSchemaIndex] : undefined; + return (
{window.initialParams.showReplayControls && ( @@ -98,34 +115,75 @@ function App() { svgIcons={Icons} /> )} - handlePanelChange(event as VSCodePanelsChangeEvent)}> - {tabPrefabs.map((tabPrefab, index) => ( - {tabPrefab.name} - ))} - {tabPrefabs.map((tabPrefab, index) => ( - - {tabPrefab.dataSource === TabPrefabDataSource.Client ? ( - - ) : ( -
- )} - {tabPrefab.dataSource === TabPrefabDataSource.ServerScript ? ( - +
+ + + {schema.map((descriptor, index) => ( + {descriptor.name} + ))} + +
+ + {activeDescriptor && ( +
+ {activeDescriptor.data_source === 'client' && ( + + )} + {activeDescriptor.data_source === 'server_script' && ( + + )} + - ) : ( -
- )} - {tabPrefab.content({ selectedClient, selectedPlugin })} - - ))} - +
+ )} +
+ ) : ( + // Static fallback: no schema received (older game version) — render hardcoded prefab tabs + handlePanelChange(event as VSCodePanelsChangeEvent)}> + {tabPrefabs.map((tabPrefab, index) => ( + {tabPrefab.name} + ))} + {tabPrefabs.map((tabPrefab, index) => ( + + {tabPrefab.dataSource === TabPrefabDataSource.Client ? ( + + ) : ( +
+ )} + {tabPrefab.dataSource === TabPrefabDataSource.ServerScript ? ( + + ) : ( +
+ )} + {tabPrefab.content({ selectedClient, selectedPlugin })} + + ))} + + )}
); } diff --git a/webview-ui/src/diagnostics_panel/DiagnosticsSchema.ts b/webview-ui/src/diagnostics_panel/DiagnosticsSchema.ts new file mode 100644 index 00000000..4988c2dd --- /dev/null +++ b/webview-ui/src/diagnostics_panel/DiagnosticsSchema.ts @@ -0,0 +1,29 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// TypeScript mirror of ScriptDiagnosticsDescriptor (C++) and its Cereal-serialized wire format. + +export type DiagnosticsDataSource = 'server' | 'client' | 'server_script'; + +export type DiagnosticsDisplayType = + | 'line_chart' + | 'stacked_line_chart' + | 'stacked_bar_chart' + | 'table' + | 'multi_column_table' + | 'dynamic_properties_table'; + +// One-to-one with ScriptDiagnosticsDescriptor fields (snake_case matches cereal wire names). +export interface DiagnosticsTabDescriptor { + name: string; + stat_group_id: string; + data_source: DiagnosticsDataSource; + display_type: DiagnosticsDisplayType; + title?: string; + y_label?: string; + tick_range?: number; + value_scalar?: number; + target_value?: number; + key_label?: string; + value_labels?: string[]; + statistic_id?: string; + statistic_ids?: string[]; +} diff --git a/webview-ui/src/diagnostics_panel/DynamicTab.tsx b/webview-ui/src/diagnostics_panel/DynamicTab.tsx new file mode 100644 index 00000000..f39ba038 --- /dev/null +++ b/webview-ui/src/diagnostics_panel/DynamicTab.tsx @@ -0,0 +1,166 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. + +// Generic tab renderer driven by a DiagnosticsTabDescriptor from the C++ schema registry. +// Derives the appropriate StatisticProvider and StatisticResolver from the descriptor fields +// and renders the matching control — no new tab files needed for new diagnostics. + +import { useMemo } from 'react'; +import { DiagnosticsTabDescriptor } from './DiagnosticsSchema'; +import { + SimpleStatisticProvider, + MultipleStatisticProvider, + StatisticProvider, +} from './StatisticProvider'; +import { StatisticType, YAxisType, createStatResolver, StatisticResolver } from './StatisticResolver'; +import MinecraftStatisticLineChart from './controls/MinecraftStatisticLineChart'; +import MinecraftStatisticStackedLineChart from './controls/MinecraftStatisticStackedLineChart'; +import MinecraftStatisticStackedBarChart from './controls/MinecraftStatisticStackedBarChart'; +import MinecraftStatisticTable from './controls/MinecraftStatisticTable'; +import MinecraftMultiColumnStatisticTable from './controls/MinecraftMultiColumnStatisticTable'; +import { MinecraftDynamicPropertiesTable } from './controls/MinecraftDynamicPropertiesTable'; + +type DynamicTabProps = { + descriptor: DiagnosticsTabDescriptor; + selectedClient: string; + selectedPlugin: string; +}; + +// Build a StatisticProvider from descriptor fields. +// Client-source tabs use a regex to match the per-player stat group prefix. +function buildProvider( + descriptor: DiagnosticsTabDescriptor, + selectedClient: string, + _selectedPlugin: string +): StatisticProvider { + const { data_source, display_type, stat_group_id, statistic_id, statistic_ids } = descriptor; + + // DynamicPropertiesTable has its own fixed provider shape + if (display_type === 'dynamic_properties_table') { + return new SimpleStatisticProvider({ + statisticId: 'consolidated_data', + statisticParentId: new RegExp(`${stat_group_id}.*`), + }); + } + + // LineChart uses SimpleStatisticProvider (single stat series) + if (display_type === 'line_chart') { + const effectiveStatId = statistic_id ?? stat_group_id; + const parentId: string | RegExp = + data_source === 'client' + ? new RegExp(`.*${selectedClient}_${stat_group_id}`) + : stat_group_id; + return new SimpleStatisticProvider({ + statisticId: effectiveStatId, + statisticParentId: parentId, + }); + } + + // All other chart/table types use MultipleStatisticProvider + const parentId: string | RegExp = + data_source === 'client' + ? new RegExp(`.*${selectedClient}_${stat_group_id}`) + : stat_group_id; + + return new MultipleStatisticProvider({ + statisticParentId: parentId, + statisticIds: statistic_ids, + }); +} + +function buildResolver(descriptor: DiagnosticsTabDescriptor): StatisticResolver { + return createStatResolver({ + type: StatisticType.Absolute, + yAxisType: YAxisType.Absolute, + tickRange: descriptor.tick_range ?? 20 * 10, + valueScalar: descriptor.value_scalar, + }); +} + +export default function DynamicTab({ descriptor, selectedClient, selectedPlugin }: DynamicTabProps) { + const title = descriptor.title ?? descriptor.name; + const yLabel = descriptor.y_label ?? ''; + const tickRange = descriptor.tick_range ?? 20 * 10; + + // Memoize provider and resolver so controls' useEffect deps remain stable across renders. + const provider = useMemo( + () => buildProvider(descriptor, selectedClient, selectedPlugin), + // re-create only when the identity of the tab or the selected client/plugin changes + [descriptor.stat_group_id, descriptor.data_source, descriptor.display_type, + descriptor.statistic_id, descriptor.statistic_ids?.join(','), selectedClient, selectedPlugin] + ); + + const resolver = useMemo( + () => buildResolver(descriptor), + [descriptor.tick_range, descriptor.value_scalar] + ); + + switch (descriptor.display_type) { + case 'line_chart': + return ( + + ); + + case 'stacked_line_chart': + return ( + + ); + + case 'stacked_bar_chart': + return ( + + ); + + case 'table': + return ( + + ); + + case 'multi_column_table': + return ( + + ); + + case 'dynamic_properties_table': + return ( + + ); + + default: + return
Unsupported display type: {descriptor.display_type}
; + } +} diff --git a/webview-ui/src/diagnostics_panel/index.tsx b/webview-ui/src/diagnostics_panel/index.tsx index 9551ae90..9b52539a 100644 --- a/webview-ui/src/diagnostics_panel/index.tsx +++ b/webview-ui/src/diagnostics_panel/index.tsx @@ -5,7 +5,7 @@ import { createRoot } from 'react-dom/client'; import App from './App'; const container = document.getElementById('root'); -const root = createRoot(container!); // createRoot(container!) if you use TypeScript +const root = createRoot(container!); root.render( diff --git a/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts b/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts index ca70a627..12fab43d 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts +++ b/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts @@ -1,5 +1,3 @@ -import { StatisticPrefab } from './StatisticPrefab'; - export type TabPrefabParams = { selectedClient: string; selectedPlugin: string; From ee74ea5a3e1b8dbe35567a170292e2e382f4a443 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Fri, 10 Apr 2026 14:33:17 -0700 Subject: [PATCH 02/16] remove descriptor changes --- src/stats/stats-provider.ts | 31 +---- webview-ui/package-lock.json | 12 +- webview-ui/src/diagnostics_panel/App.tsx | 114 +++++------------- webview-ui/src/diagnostics_panel/index.tsx | 2 +- .../diagnostics_panel/prefabs/TabPrefab.ts | 2 + 5 files changed, 43 insertions(+), 118 deletions(-) diff --git a/src/stats/stats-provider.ts b/src/stats/stats-provider.ts index 0b8c664d..ddace2ff 100644 --- a/src/stats/stats-provider.ts +++ b/src/stats/stats-provider.ts @@ -26,35 +26,12 @@ export interface StatMessageModel { stats: StatDataModel[]; } -// Mirrors ScriptDiagnosticsDescriptor from C++. Sent once on connect via SchemaEvent. -export interface DiagnosticsTabDescriptor { - name: string; - stat_group_id: string; - data_source: 'server' | 'client' | 'server_script'; - display_type: 'line_chart' | 'stacked_line_chart' | 'stacked_bar_chart' | 'table' | 'multi_column_table' | 'dynamic_properties_table'; - title?: string; - y_label?: string; - tick_range?: number; - value_scalar?: number; - target_value?: number; - key_label?: string; - value_labels?: string[]; - statistic_id?: string; - statistic_ids?: string[]; -} - -export interface SchemaMessageModel { - type: 'SchemaEvent'; - descriptors: DiagnosticsTabDescriptor[]; -} - export interface StatsListener { onStatUpdated?: (stat: StatData) => void; onSpeedUpdated?: (speed: number) => void; onPauseUpdated?: (paused: boolean) => void; onStopped?: () => void; onNotification?: (message: string) => void; - onSchemaReceived?: (schema: DiagnosticsTabDescriptor[]) => void; } export class StatsProvider { @@ -69,13 +46,7 @@ export class StatsProvider { this._fireStatUpdated(stat, stats.tick); } } - - public setSchema(schema: DiagnosticsTabDescriptor[]): void { - this._statListeners.forEach((listener: StatsListener) => { - listener.onSchemaReceived?.(schema); - }); - } - + public start(): void { throw new Error('Method not implemented.'); } diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index 9891a45a..d0dce43b 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -69,6 +69,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -1526,6 +1527,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.20.0" } @@ -1630,6 +1632,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001629", "electron-to-chromium": "^1.4.796", @@ -2005,6 +2008,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "peer": true, "engines": { "node": ">=12" } @@ -2432,6 +2436,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -2762,6 +2767,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", "dev": true, + "peer": true, "requires": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -3708,6 +3714,7 @@ "version": "22.13.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", + "peer": true, "requires": { "undici-types": "~6.20.0" } @@ -3789,6 +3796,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", "dev": true, + "peer": true, "requires": { "caniuse-lite": "^1.0.30001629", "electron-to-chromium": "^1.4.796", @@ -4053,7 +4061,8 @@ "d3-selection": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "peer": true }, "d3-shape": { "version": "3.2.0", @@ -4347,6 +4356,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "requires": { "loose-envify": "^1.1.0" } diff --git a/webview-ui/src/diagnostics_panel/App.tsx b/webview-ui/src/diagnostics_panel/App.tsx index d41e66fc..51f6d232 100644 --- a/webview-ui/src/diagnostics_panel/App.tsx +++ b/webview-ui/src/diagnostics_panel/App.tsx @@ -1,6 +1,6 @@ // Copyright (C) Microsoft Corporation. All rights reserved. -import { VSCodeDropdown, VSCodeOption, VSCodePanelTab, VSCodePanelView, VSCodePanels } from '@vscode/webview-ui-toolkit/react'; +import { VSCodePanelTab, VSCodePanelView, VSCodePanels } from '@vscode/webview-ui-toolkit/react'; import { StatGroupSelectionBox } from './controls/StatGroupSelectionBox'; import { useCallback, useEffect, useState } from 'react'; import { StatisticType, YAxisStyle, YAxisType, createStatResolver } from './StatisticResolver'; @@ -14,8 +14,6 @@ import { Icons } from './Icons'; import './App.css'; import tabPrefabs from './prefabs'; import { TabPrefabDataSource } from './prefabs/TabPrefab'; -import { DiagnosticsTabDescriptor } from './DiagnosticsSchema'; -import DynamicTab from './DynamicTab'; declare global { interface Window { @@ -55,10 +53,6 @@ function App() { const [currentTab, setCurrentTab] = useState(); const [paused, setPaused] = useState(true); const [speed, setSpeed] = useState(''); - // Dynamic schema received from the game on connect. Empty = use static prefab fallback. - const [schema, setSchema] = useState([]); - // Index of the selected tab in the dynamic dropdown - const [selectedSchemaIndex, setSelectedSchemaIndex] = useState(0); const handlePluginSelection = useCallback((pluginSelectionId: string) => { setSelectedPlugin(() => pluginSelectionId); @@ -75,11 +69,6 @@ function App() { } }, []); - const handleSchemaTabChange = useCallback((e: Event | React.FormEvent): void => { - const target = e.target as HTMLSelectElement; - setSelectedSchemaIndex(target.selectedIndex); - }, []); - useEffect(() => { const handleMessage = (event: MessageEvent) => { const message = event.data; @@ -87,9 +76,6 @@ function App() { setSpeed(`${message.speed}hz`); } else if (message.type === 'pause-updated') { setPaused(message.paused); - } else if (message.type === 'diagnostics-schema') { - setSchema(message.schema as DiagnosticsTabDescriptor[]); - setSelectedSchemaIndex(0); } }; window.addEventListener('message', handleMessage); @@ -98,9 +84,6 @@ function App() { }; }, []); - const usingDynamicSchema = schema.length > 0; - const activeDescriptor = usingDynamicSchema ? schema[selectedSchemaIndex] : undefined; - return (
{window.initialParams.showReplayControls && ( @@ -115,75 +98,34 @@ function App() { svgIcons={Icons} /> )} - - {usingDynamicSchema ? ( - // Dynamic mode: schema received from game — dropdown selector + single DynamicTab -
-
- - - {schema.map((descriptor, index) => ( - {descriptor.name} - ))} - -
- - {activeDescriptor && ( -
- {activeDescriptor.data_source === 'client' && ( - - )} - {activeDescriptor.data_source === 'server_script' && ( - - )} - handlePanelChange(event as VSCodePanelsChangeEvent)}> + {tabPrefabs.map((tabPrefab, index) => ( + {tabPrefab.name} + ))} + {tabPrefabs.map((tabPrefab, index) => ( + + {tabPrefab.dataSource === TabPrefabDataSource.Client ? ( + -
- )} -
- ) : ( - // Static fallback: no schema received (older game version) — render hardcoded prefab tabs - handlePanelChange(event as VSCodePanelsChangeEvent)}> - {tabPrefabs.map((tabPrefab, index) => ( - {tabPrefab.name} - ))} - {tabPrefabs.map((tabPrefab, index) => ( - - {tabPrefab.dataSource === TabPrefabDataSource.Client ? ( - - ) : ( -
- )} - {tabPrefab.dataSource === TabPrefabDataSource.ServerScript ? ( - - ) : ( -
- )} - {tabPrefab.content({ selectedClient, selectedPlugin })} - - ))} - - )} + ) : ( +
+ )} + {tabPrefab.dataSource === TabPrefabDataSource.ServerScript ? ( + + ) : ( +
+ )} + {tabPrefab.content({ selectedClient, selectedPlugin })} + + ))} +
); } diff --git a/webview-ui/src/diagnostics_panel/index.tsx b/webview-ui/src/diagnostics_panel/index.tsx index 9b52539a..9551ae90 100644 --- a/webview-ui/src/diagnostics_panel/index.tsx +++ b/webview-ui/src/diagnostics_panel/index.tsx @@ -5,7 +5,7 @@ import { createRoot } from 'react-dom/client'; import App from './App'; const container = document.getElementById('root'); -const root = createRoot(container!); +const root = createRoot(container!); // createRoot(container!) if you use TypeScript root.render( diff --git a/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts b/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts index 12fab43d..ca70a627 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts +++ b/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts @@ -1,3 +1,5 @@ +import { StatisticPrefab } from './StatisticPrefab'; + export type TabPrefabParams = { selectedClient: string; selectedPlugin: string; From 870c27c16ac58a3e7754e1ee451ae51ede1e56e8 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Tue, 12 May 2026 11:51:37 -0700 Subject: [PATCH 03/16] fixes --- CONTRIBUTING.md | 11 +- package-lock.json | 82 ++++-------- src/breakpoints-legacy.ts | 5 +- src/debuggee-message-sender.ts | 3 +- src/panels/minecraft-diagnostics.ts | 6 - src/protocol-events.ts | 195 ++++++++++++++++++++++++++++ src/session.ts | 135 ++++++++++--------- 7 files changed, 299 insertions(+), 138 deletions(-) create mode 100644 src/protocol-events.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 441ef5da..470bffc5 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 was 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/package-lock.json b/package-lock.json index 9183e6ce..716e8130 100644 --- a/package-lock.json +++ b/package-lock.json @@ -867,7 +867,6 @@ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -883,7 +882,6 @@ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.0.0" } @@ -894,7 +892,6 @@ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.0.0" } @@ -905,7 +902,6 @@ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -924,7 +920,6 @@ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1035,6 +1030,7 @@ "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.0.0", @@ -2186,6 +2182,7 @@ "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "4.33.0", "@typescript-eslint/types": "4.33.0", @@ -2677,7 +2674,6 @@ "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" @@ -2688,24 +2684,21 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.6", @@ -2713,7 +2706,6 @@ "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", @@ -2725,8 +2717,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.12.1", @@ -2734,7 +2725,6 @@ "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -2748,7 +2738,6 @@ "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -2759,7 +2748,6 @@ "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -2769,8 +2757,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.12.1", @@ -2778,7 +2765,6 @@ "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -2796,7 +2782,6 @@ "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", @@ -2811,7 +2796,6 @@ "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -2825,7 +2809,6 @@ "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", @@ -2841,7 +2824,6 @@ "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" @@ -2852,16 +2834,14 @@ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true, - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/acorn": { "version": "8.13.0", @@ -2920,6 +2900,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2937,7 +2918,6 @@ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -3279,8 +3259,7 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/cac": { "version": "6.7.14", @@ -3352,8 +3331,7 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "CC-BY-4.0", - "peer": true + "license": "CC-BY-4.0" }, "node_modules/chai": { "version": "5.1.2", @@ -3490,7 +3468,6 @@ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.0" } @@ -4210,8 +4187,7 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.41.tgz", "integrity": "sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -4547,6 +4523,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -4715,6 +4692,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", @@ -5600,8 +5578,7 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true, - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/globals": { "version": "13.24.0", @@ -6218,7 +6195,6 @@ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -6234,7 +6210,6 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -6245,7 +6220,6 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -6500,7 +6474,6 @@ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.11.5" } @@ -6623,7 +6596,6 @@ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -6685,6 +6657,7 @@ "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", "dev": true, "license": "MIT", + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -7005,8 +6978,7 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/normalize-package-data": { "version": "6.0.2", @@ -10861,7 +10833,6 @@ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -11239,7 +11210,6 @@ "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -11259,6 +11229,7 @@ "integrity": "sha512-Cb0Pm3Ye15u8k/B+7EnusMUSIIucAIEBD3QDRmmclv53KVyqmg1Lb3XPx0AMNxfJZEI+ZT+M+IXDyTrudK6Rew==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@semantic-release/commit-analyzer": "^13.0.0-beta.1", "@semantic-release/error": "^4.0.0", @@ -11828,7 +11799,6 @@ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -12126,7 +12096,6 @@ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -12138,7 +12107,6 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -12611,7 +12579,6 @@ "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -12631,7 +12598,6 @@ "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", @@ -12666,8 +12632,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/text-table": { "version": "0.2.0", @@ -13001,6 +12966,7 @@ "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13130,7 +13096,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.0" @@ -13200,6 +13165,7 @@ "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -13779,7 +13745,6 @@ "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -13794,7 +13759,6 @@ "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", @@ -13842,7 +13806,6 @@ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.13.0" } @@ -13853,7 +13816,6 @@ "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "acorn": "^8" } diff --git a/src/breakpoints-legacy.ts b/src/breakpoints-legacy.ts index 0756fe07..8d368385 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,8 +68,8 @@ 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', + const envelope: BreakpointsMessage = { + type: OutgoingEventType.breakpoints, breakpoints: { path: generatedRemoteLocalPath, breakpoints: generatedBreakpoints.length ? generatedBreakpoints : undefined, diff --git a/src/debuggee-message-sender.ts b/src/debuggee-message-sender.ts index 73ea598b..03707083 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/panels/minecraft-diagnostics.ts b/src/panels/minecraft-diagnostics.ts index bce18d0d..b535c058 100644 --- a/src/panels/minecraft-diagnostics.ts +++ b/src/panels/minecraft-diagnostics.ts @@ -96,12 +96,6 @@ export class MinecraftDiagnosticsPanel { onNotification: (message: string) => { window.showInformationMessage(message); }, - onSchemaReceived: (schema) => { - this._panel.webview.postMessage({ - type: 'diagnostics-schema', - schema, - }); - }, }; this._statsTracker.addStatListener(this._statsCallback); diff --git a/src/protocol-events.ts b/src/protocol-events.ts new file mode 100644 index 00000000..a3d61646 --- /dev/null +++ b/src/protocol-events.ts @@ -0,0 +1,195 @@ +// 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 - New serialization tech (use Cereal) + +export enum ProtocolVersion { + _Unknown = 0, + Initial = 1, + SupportTargetModuleUuid = 2, + SupportTargetSelection = 3, + SupportPasscode = 4, + SupportProfilerCaptures = 5, + SupportBreakpointsAsRequest = 6, + SupportCerealSerialization = 7, +} + +export const DEBUGGER_PROTOCOL_VERSION = ProtocolVersion.SupportCerealSerialization; + +// ------------------------------------------------------------------------- +// Interfaces for event message payloads (received from the debugee) +// ------------------------------------------------------------------------- +export const IncomingEventType = { + stopped: 'StoppedEvent', + thread: 'ThreadEvent', + print: 'PrintEvent', + notification: 'NotificationEvent', + protocol: 'ProtocolEvent', + stat2: 'StatEvent2', + schema: 'SchemaEvent', + profilerCapture: 'ProfilerCapture', +} as const; + +export interface PluginDetails { + name: string; + module_uuid: string; +} + +export interface ProtocolCapabilities { + type: typeof IncomingEventType.protocol; + version: number; + plugins: PluginDetails[]; + require_passcode?: boolean; +} + +export interface ProfilerCapture { + type: typeof IncomingEventType.profilerCapture; + capture_base_path: string; + capture_data: string; +} + +export interface StoppedEventMessage { + type: typeof IncomingEventType.stopped; + reason: string; + thread: number; +} + +export interface ThreadEventMessage { + type: typeof IncomingEventType.thread; + reason: string; + thread: number; +} + +export interface PrintEventMessage { + type: typeof IncomingEventType.print; + message: string; + logLevel: LogLevel; +} + +export interface NotificationEventMessage { + type: typeof IncomingEventType.notification; + message: string; + logLevel: LogLevel; +} + +export type IncomingDebuggeeMessage = + | PluginDetails + | ProtocolCapabilities + | ProfilerCapture + | StoppedEventMessage + | ThreadEventMessage + | PrintEventMessage + | NotificationEventMessage; + + + +// ------------------------------------------------------------------------- +// Interfaces for outbound message payloads (sent to the debugee) +// ------------------------------------------------------------------------- +export const OutgoingEventType = { + protocol: 'protocol', + minecraftCommand: 'minecraftCommand', + startProfiler: 'startProfiler', + stopProfiler: 'stopProfiler', + stopOnException: 'stopOnException', + resume: 'resume', + request: 'request', + breakpoints: 'breakpoints', +} as const; + +export interface ProtocolResponse { + type: typeof OutgoingEventType.protocol; + version: number; + target_module_uuid?: string; + passcode?: string; +} + +export interface MinecraftCommandLegacyMessage { + type: typeof OutgoingEventType.minecraftCommand; + command: string; + dimension_type: string; +} + +export interface MinecraftCommandMessage { + type: typeof OutgoingEventType.minecraftCommand; + command: { command: string; dimension_type: string }; +} + +export interface StartProfilerMessage { + type: typeof OutgoingEventType.startProfiler; + profiler: { target_module_uuid?: string }; +} + +export interface StopProfilerMessage { + type: typeof OutgoingEventType.stopProfiler; + profiler: { captures_path: string; target_module_uuid?: string }; +} + +export interface StopOnExceptionMessage { + type: typeof OutgoingEventType.stopOnException; + stopOnException: boolean; +} + +export interface ResumeMessage { + type: typeof OutgoingEventType.resume; +} + +export interface RequestMessage { + type: typeof OutgoingEventType.request; + request: { request_seq: number; command: string; args: unknown }; +} + +export interface BreakpointsMessage { + type: typeof OutgoingEventType.breakpoints; + breakpoints: { path: string; breakpoints: DebugProtocol.SourceBreakpoint[] | undefined }; +} + +export type OutgoingDebuggeeMessage = + | ProtocolResponse + | MinecraftCommandLegacyMessage + | MinecraftCommandMessage + | StartProfilerMessage + | StopProfilerMessage + | StopOnExceptionMessage + | ResumeMessage + | RequestMessage + | BreakpointsMessage; + + + +// Re-export for callers that need these alongside event types +export type { StatMessageModel }; + +// ------------------------------------------------------------------------- +// Registry that maps event type name strings to handler callbacks +// ------------------------------------------------------------------------- + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type DebuggeeEventHandler = (event: any) => void; + +export class DebuggeeEventRegistry { + private readonly _handlers = new Map(); + + public register(eventType: string, handler: DebuggeeEventHandler): void { + this._handlers.set(eventType, handler); + } + + public dispatch(eventMessage: { type: string }): boolean { + const handler = this._handlers.get(eventMessage.type); + if (handler) { + handler(eventMessage); + return true; + } + return false; + } +} diff --git a/src/session.ts b/src/session.ts index 3a6ce906..cd94e474 100644 --- a/src/session.ts +++ b/src/session.ts @@ -42,6 +42,22 @@ import { IBreakpointsHandler } from './ibreakpoints-handler'; import { injectSourceMapIntoProfilerCapture } from './profiler-utils'; import { isUUID } from './utils'; import { MessageStreamParser } from './message-stream-parser'; +import { + DebuggeeEventRegistry, + DEBUGGER_PROTOCOL_VERSION, + IncomingEventType, + NotificationEventMessage, + OutgoingDebuggeeMessage, + OutgoingEventType, + PluginDetails, + PrintEventMessage, + ProfilerCapture, + ProtocolCapabilities, + ProtocolVersion, + RequestMessage, + StoppedEventMessage, + ThreadEventMessage, +} from './protocol-events'; import { SourceMaps } from './source-maps'; import { StatMessageModel, StatsProvider } from './stats/stats-provider'; @@ -55,24 +71,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 { @@ -108,23 +106,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 -enum ProtocolVersion { - _Unknown = 0, - Initial = 1, - SupportTargetModuleUuid = 2, - SupportTargetSelection = 3, - SupportPasscode = 4, - SupportProfilerCaptures = 5, - SupportBreakpointsAsRequest = 6, -} - // capabilites based on protocol version export interface MinecraftCapabilities { supportsCommands: boolean; @@ -135,10 +116,9 @@ export interface MinecraftCapabilities { // The Debug Adapter for 'minecraft-js' // export class Session extends DebugSession implements IDebuggeeMessageSender { - private readonly _debuggerProtocolVersion = ProtocolVersion.SupportBreakpointsAsRequest; + private readonly _connectionRetryAttempts = 3; private readonly _connectionRetryWaitMs = 500; - private _debugeeServer?: Server; // when listening for incoming connections private _connectionSocket?: Socket; private _connected = false; @@ -162,6 +142,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { supportsBreakpointsAsRequest: false, }; private _passcode?: string; + private _eventRegistry: DebuggeeEventRegistry; // external communication private _homeViewProvider: HomeViewProvider; @@ -178,12 +159,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'); @@ -203,13 +217,13 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { private onRunMinecraftCommand(command: string): void { if (this._clientProtocolVersion < ProtocolVersion.SupportProfilerCaptures) { 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', @@ -220,7 +234,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { private onStartProfiler(): void { this.sendDebuggeeMessage({ - type: 'startProfiler', + type: OutgoingEventType.startProfiler, profiler: { target_module_uuid: this._targetModuleUuid, }, @@ -229,7 +243,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { private onStopProfiler(capturesBasePath: string): void { this.sendDebuggeeMessage({ - type: 'stopProfiler', + type: OutgoingEventType.stopProfiler, profiler: { captures_path: capturesBasePath, target_module_uuid: this._targetModuleUuid, @@ -430,7 +444,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 }); @@ -439,7 +453,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse): void { this.sendDebuggeeMessage({ - type: 'resume', + type: OutgoingEventType.resume, }); this.sendResponse(response); @@ -730,7 +744,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, @@ -838,9 +852,9 @@ 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', + private makeRequestPayload(requestSeq: number, responseCommand: string, args: unknown): RequestMessage { + const envelope: RequestMessage = { + type: OutgoingEventType.request, request: { request_seq: requestSeq, command: responseCommand, @@ -850,7 +864,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { return envelope; } - public sendDebuggeeMessage(envelope: unknown): void { + public sendDebuggeeMessage(envelope: OutgoingDebuggeeMessage): void { if (!this._connectionSocket) { return; } @@ -876,29 +890,14 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { } 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 === 'SchemaEvent') { - this._statsProvider.setSchema(eventMessage.descriptors); - } else if (eventMessage.type === 'ProfilerCapture') { - this.handleProfilerCapture(eventMessage as ProfilerCapture); - } + this._eventRegistry.dispatch(eventMessage); } private async handlePrintEvent(message: string, logLevel: LogLevel) { @@ -967,7 +966,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 From 4553d18ea01538948013c77c1c97e0e701368ac2 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Fri, 22 May 2026 16:34:32 -0700 Subject: [PATCH 04/16] merge --- src/protocol-events.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/protocol-events.ts b/src/protocol-events.ts index a3d61646..092f11bb 100644 --- a/src/protocol-events.ts +++ b/src/protocol-events.ts @@ -11,7 +11,8 @@ import { StatMessageModel } from './stats/stats-provider'; // 4 - mc can require a passcode to connect // 5 - debugger can take mc script profiler captures // 6 - breakpoints as request, MC can reject -// 7 - New serialization tech (use Cereal) +// 7 - support for debugger requests, MC can reject or respond with args +// 8 - New serialization tech (use Cereal) export enum ProtocolVersion { _Unknown = 0, @@ -21,7 +22,8 @@ export enum ProtocolVersion { SupportPasscode = 4, SupportProfilerCaptures = 5, SupportBreakpointsAsRequest = 6, - SupportCerealSerialization = 7, + SupportDebuggerRequests = 7, + SupportCerealSerialization = 8, } export const DEBUGGER_PROTOCOL_VERSION = ProtocolVersion.SupportCerealSerialization; From 50c52ba3ac737a255d8e6e511593ebece94fa70c Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Tue, 26 May 2026 16:12:19 -0700 Subject: [PATCH 05/16] fixes/merge --- src/protocol-events.ts | 24 ++++++++++++++++--- src/requests/debugger-request-schema.ts | 21 +--------------- src/requests/request-manager.ts | 3 ++- src/session.ts | 3 ++- .../utilities/useDebuggerRequests.ts | 2 +- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/protocol-events.ts b/src/protocol-events.ts index 092f11bb..a2f316aa 100644 --- a/src/protocol-events.ts +++ b/src/protocol-events.ts @@ -84,6 +84,14 @@ export interface NotificationEventMessage { logLevel: LogLevel; } +export interface DebuggeeResponseEnvelope { + type: 'debuggee-response'; + request_seq: number; + args?: unknown; + success?: boolean; + response_message?: string; +} + export type IncomingDebuggeeMessage = | PluginDetails | ProtocolCapabilities @@ -91,8 +99,8 @@ export type IncomingDebuggeeMessage = | StoppedEventMessage | ThreadEventMessage | PrintEventMessage - | NotificationEventMessage; - + | NotificationEventMessage + | DebuggeeResponseEnvelope; // ------------------------------------------------------------------------- @@ -156,6 +164,15 @@ export interface BreakpointsMessage { breakpoints: { path: string; breakpoints: DebugProtocol.SourceBreakpoint[] | undefined }; } +export interface DebuggerRequestEnvelope { + type: 'debugger-request'; + request: { + request_seq: number; + request: string; + args?: unknown; + }; +} + export type OutgoingDebuggeeMessage = | ProtocolResponse | MinecraftCommandLegacyMessage @@ -165,7 +182,8 @@ export type OutgoingDebuggeeMessage = | StopOnExceptionMessage | ResumeMessage | RequestMessage - | BreakpointsMessage; + | BreakpointsMessage + | DebuggerRequestEnvelope; diff --git a/src/requests/debugger-request-schema.ts b/src/requests/debugger-request-schema.ts index 5b0a73ed..81aa67b8 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.ts b/src/requests/request-manager.ts index 78264210..b33d6eb9 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, DebuggerRequestEnvelope } from '../protocol-events'; interface PendingDebuggerRequest { resolve: (value: DebuggeeResponseEnvelope) => void; diff --git a/src/session.ts b/src/session.ts index 451532b3..08bd39ce 100644 --- a/src/session.ts +++ b/src/session.ts @@ -57,11 +57,12 @@ import { RequestMessage, StoppedEventMessage, ThreadEventMessage, + DebuggeeResponseEnvelope } from './protocol-events'; import { SourceMaps } from './source-maps'; import { StatMessageModel, StatsProvider } from './stats/stats-provider'; import { RequestManager } from './requests/request-manager'; -import { DebuggeeResponseEnvelope, DebuggerRequestArguments } from './requests/debugger-request-schema'; +import { DebuggerRequestArguments } from './requests/debugger-request-schema'; interface PendingResponse { onSuccess?: (result: any) => void; diff --git a/webview-ui/src/diagnostics_panel/utilities/useDebuggerRequests.ts b/webview-ui/src/diagnostics_panel/utilities/useDebuggerRequests.ts index 6efdada8..ba212287 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. From 863d46dfbf3e9a5bf63b5027063bd509bc625113 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Wed, 27 May 2026 13:32:48 -0700 Subject: [PATCH 06/16] fixes --- src/protocol-events.ts | 6 ++++-- src/requests/request-manager.ts | 4 ++-- src/session.ts | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/protocol-events.ts b/src/protocol-events.ts index a2f316aa..7b878ca7 100644 --- a/src/protocol-events.ts +++ b/src/protocol-events.ts @@ -40,6 +40,7 @@ export const IncomingEventType = { stat2: 'StatEvent2', schema: 'SchemaEvent', profilerCapture: 'ProfilerCapture', + debuggeeResponse: 'debuggee-response', } as const; export interface PluginDetails { @@ -85,7 +86,7 @@ export interface NotificationEventMessage { } export interface DebuggeeResponseEnvelope { - type: 'debuggee-response'; + type: typeof IncomingEventType.debuggeeResponse; request_seq: number; args?: unknown; success?: boolean; @@ -115,6 +116,7 @@ export const OutgoingEventType = { resume: 'resume', request: 'request', breakpoints: 'breakpoints', + debuggerRequest: 'debugger-request' } as const; export interface ProtocolResponse { @@ -165,7 +167,7 @@ export interface BreakpointsMessage { } export interface DebuggerRequestEnvelope { - type: 'debugger-request'; + type: typeof OutgoingEventType.debuggerRequest; request: { request_seq: number; request: string; diff --git a/src/requests/request-manager.ts b/src/requests/request-manager.ts index b33d6eb9..53225427 100644 --- a/src/requests/request-manager.ts +++ b/src/requests/request-manager.ts @@ -3,7 +3,7 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import { DebuggerRequestArguments} from './debugger-request-schema'; import { IDebuggeeMessageSender } from '../debuggee-message-sender'; -import { DebuggeeResponseEnvelope, DebuggerRequestEnvelope } from '../protocol-events'; +import { DebuggeeResponseEnvelope, DebuggerRequestEnvelope, OutgoingEventType } from '../protocol-events'; interface PendingDebuggerRequest { resolve: (value: DebuggeeResponseEnvelope) => void; @@ -47,7 +47,7 @@ export class RequestManager { // Create an envelope to hold the request, and send it to the debuggee const envelope: DebuggerRequestEnvelope = { - type: 'debugger-request', + type: OutgoingEventType.debuggerRequest, request: { request_seq: seq, request, diff --git a/src/session.ts b/src/session.ts index 08bd39ce..d23c8669 100644 --- a/src/session.ts +++ b/src/session.ts @@ -927,7 +927,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.', From e462c4794c12d203f8992a8815f9bfa80fd9237d Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Wed, 27 May 2026 14:23:28 -0700 Subject: [PATCH 07/16] Update session.ts --- src/session.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/session.ts b/src/session.ts index d23c8669..d7ec2cd9 100644 --- a/src/session.ts +++ b/src/session.ts @@ -936,7 +936,7 @@ 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); } From fa1d26eaed29d96508a8f6818d76d1a9526909c3 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Wed, 27 May 2026 14:56:10 -0700 Subject: [PATCH 08/16] remove --- .../diagnostics_panel/DiagnosticsSchema.ts | 29 --- .../src/diagnostics_panel/DynamicTab.tsx | 166 ------------------ 2 files changed, 195 deletions(-) delete mode 100644 webview-ui/src/diagnostics_panel/DiagnosticsSchema.ts delete mode 100644 webview-ui/src/diagnostics_panel/DynamicTab.tsx diff --git a/webview-ui/src/diagnostics_panel/DiagnosticsSchema.ts b/webview-ui/src/diagnostics_panel/DiagnosticsSchema.ts deleted file mode 100644 index 4988c2dd..00000000 --- a/webview-ui/src/diagnostics_panel/DiagnosticsSchema.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// TypeScript mirror of ScriptDiagnosticsDescriptor (C++) and its Cereal-serialized wire format. - -export type DiagnosticsDataSource = 'server' | 'client' | 'server_script'; - -export type DiagnosticsDisplayType = - | 'line_chart' - | 'stacked_line_chart' - | 'stacked_bar_chart' - | 'table' - | 'multi_column_table' - | 'dynamic_properties_table'; - -// One-to-one with ScriptDiagnosticsDescriptor fields (snake_case matches cereal wire names). -export interface DiagnosticsTabDescriptor { - name: string; - stat_group_id: string; - data_source: DiagnosticsDataSource; - display_type: DiagnosticsDisplayType; - title?: string; - y_label?: string; - tick_range?: number; - value_scalar?: number; - target_value?: number; - key_label?: string; - value_labels?: string[]; - statistic_id?: string; - statistic_ids?: string[]; -} diff --git a/webview-ui/src/diagnostics_panel/DynamicTab.tsx b/webview-ui/src/diagnostics_panel/DynamicTab.tsx deleted file mode 100644 index f39ba038..00000000 --- a/webview-ui/src/diagnostics_panel/DynamicTab.tsx +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. - -// Generic tab renderer driven by a DiagnosticsTabDescriptor from the C++ schema registry. -// Derives the appropriate StatisticProvider and StatisticResolver from the descriptor fields -// and renders the matching control — no new tab files needed for new diagnostics. - -import { useMemo } from 'react'; -import { DiagnosticsTabDescriptor } from './DiagnosticsSchema'; -import { - SimpleStatisticProvider, - MultipleStatisticProvider, - StatisticProvider, -} from './StatisticProvider'; -import { StatisticType, YAxisType, createStatResolver, StatisticResolver } from './StatisticResolver'; -import MinecraftStatisticLineChart from './controls/MinecraftStatisticLineChart'; -import MinecraftStatisticStackedLineChart from './controls/MinecraftStatisticStackedLineChart'; -import MinecraftStatisticStackedBarChart from './controls/MinecraftStatisticStackedBarChart'; -import MinecraftStatisticTable from './controls/MinecraftStatisticTable'; -import MinecraftMultiColumnStatisticTable from './controls/MinecraftMultiColumnStatisticTable'; -import { MinecraftDynamicPropertiesTable } from './controls/MinecraftDynamicPropertiesTable'; - -type DynamicTabProps = { - descriptor: DiagnosticsTabDescriptor; - selectedClient: string; - selectedPlugin: string; -}; - -// Build a StatisticProvider from descriptor fields. -// Client-source tabs use a regex to match the per-player stat group prefix. -function buildProvider( - descriptor: DiagnosticsTabDescriptor, - selectedClient: string, - _selectedPlugin: string -): StatisticProvider { - const { data_source, display_type, stat_group_id, statistic_id, statistic_ids } = descriptor; - - // DynamicPropertiesTable has its own fixed provider shape - if (display_type === 'dynamic_properties_table') { - return new SimpleStatisticProvider({ - statisticId: 'consolidated_data', - statisticParentId: new RegExp(`${stat_group_id}.*`), - }); - } - - // LineChart uses SimpleStatisticProvider (single stat series) - if (display_type === 'line_chart') { - const effectiveStatId = statistic_id ?? stat_group_id; - const parentId: string | RegExp = - data_source === 'client' - ? new RegExp(`.*${selectedClient}_${stat_group_id}`) - : stat_group_id; - return new SimpleStatisticProvider({ - statisticId: effectiveStatId, - statisticParentId: parentId, - }); - } - - // All other chart/table types use MultipleStatisticProvider - const parentId: string | RegExp = - data_source === 'client' - ? new RegExp(`.*${selectedClient}_${stat_group_id}`) - : stat_group_id; - - return new MultipleStatisticProvider({ - statisticParentId: parentId, - statisticIds: statistic_ids, - }); -} - -function buildResolver(descriptor: DiagnosticsTabDescriptor): StatisticResolver { - return createStatResolver({ - type: StatisticType.Absolute, - yAxisType: YAxisType.Absolute, - tickRange: descriptor.tick_range ?? 20 * 10, - valueScalar: descriptor.value_scalar, - }); -} - -export default function DynamicTab({ descriptor, selectedClient, selectedPlugin }: DynamicTabProps) { - const title = descriptor.title ?? descriptor.name; - const yLabel = descriptor.y_label ?? ''; - const tickRange = descriptor.tick_range ?? 20 * 10; - - // Memoize provider and resolver so controls' useEffect deps remain stable across renders. - const provider = useMemo( - () => buildProvider(descriptor, selectedClient, selectedPlugin), - // re-create only when the identity of the tab or the selected client/plugin changes - [descriptor.stat_group_id, descriptor.data_source, descriptor.display_type, - descriptor.statistic_id, descriptor.statistic_ids?.join(','), selectedClient, selectedPlugin] - ); - - const resolver = useMemo( - () => buildResolver(descriptor), - [descriptor.tick_range, descriptor.value_scalar] - ); - - switch (descriptor.display_type) { - case 'line_chart': - return ( - - ); - - case 'stacked_line_chart': - return ( - - ); - - case 'stacked_bar_chart': - return ( - - ); - - case 'table': - return ( - - ); - - case 'multi_column_table': - return ( - - ); - - case 'dynamic_properties_table': - return ( - - ); - - default: - return
Unsupported display type: {descriptor.display_type}
; - } -} From 4614baa85e63418017a92a319f0f0b9c7400bcd9 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Wed, 27 May 2026 17:06:39 -0700 Subject: [PATCH 09/16] feedback --- CONTRIBUTING.md | 2 +- package-lock.json | 80 +++++++++++++++------- src/breakpoints-legacy.ts | 2 +- src/protocol-events.ts | 99 +++++++++++++++------------- src/requests/request-manager.test.ts | 9 +-- src/requests/request-manager.ts | 2 +- src/session.ts | 32 ++++----- src/stats/stats-provider.ts | 2 +- 8 files changed, 136 insertions(+), 92 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 470bffc5..3ac1ed5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,7 +20,7 @@ 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 was automatically build and run the extension. +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 diff --git a/package-lock.json b/package-lock.json index 68c8ae70..d3527aeb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -918,6 +918,7 @@ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -933,6 +934,7 @@ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.0.0" } @@ -943,6 +945,7 @@ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.0.0" } @@ -953,6 +956,7 @@ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -971,6 +975,7 @@ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1362,7 +1367,6 @@ "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.0.0", @@ -2909,7 +2913,6 @@ "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "4.33.0", "@typescript-eslint/types": "4.33.0", @@ -3493,6 +3496,7 @@ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -3503,21 +3507,24 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", @@ -3525,6 +3532,7 @@ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -3536,7 +3544,8 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", @@ -3544,6 +3553,7 @@ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -3557,6 +3567,7 @@ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -3567,6 +3578,7 @@ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -3576,7 +3588,8 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", @@ -3584,6 +3597,7 @@ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -3601,6 +3615,7 @@ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -3615,6 +3630,7 @@ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -3628,6 +3644,7 @@ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -3643,6 +3660,7 @@ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -3653,14 +3671,16 @@ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/acorn": { "version": "8.15.0", @@ -3733,7 +3753,6 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4162,7 +4181,8 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/cac": { "version": "6.7.14", @@ -4234,7 +4254,8 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "CC-BY-4.0" + "license": "CC-BY-4.0", + "peer": true }, "node_modules/chai": { "version": "5.1.2", @@ -4371,6 +4392,7 @@ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.0" } @@ -5143,7 +5165,8 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -5479,7 +5502,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -5648,7 +5670,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", @@ -6524,7 +6545,8 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true, - "license": "BSD-2-Clause" + "license": "BSD-2-Clause", + "peer": true }, "node_modules/globals": { "version": "13.24.0", @@ -7202,6 +7224,7 @@ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -7217,6 +7240,7 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -7227,6 +7251,7 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -7494,6 +7519,7 @@ "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.11.5" }, @@ -7620,6 +7646,7 @@ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -7681,7 +7708,6 @@ "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", "dev": true, "license": "MIT", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -8002,7 +8028,8 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/node-sarif-builder": { "version": "3.4.0", @@ -11892,6 +11919,7 @@ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -12282,6 +12310,7 @@ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -12434,7 +12463,6 @@ "integrity": "sha512-Cb0Pm3Ye15u8k/B+7EnusMUSIIucAIEBD3QDRmmclv53KVyqmg1Lb3XPx0AMNxfJZEI+ZT+M+IXDyTrudK6Rew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@semantic-release/commit-analyzer": "^13.0.0-beta.1", "@semantic-release/error": "^4.0.0", @@ -12869,6 +12897,7 @@ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -13176,6 +13205,7 @@ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -13187,6 +13217,7 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -13690,6 +13721,7 @@ "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -13709,6 +13741,7 @@ "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -13743,7 +13776,8 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/text-table": { "version": "0.2.0", @@ -14093,7 +14127,6 @@ "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14223,6 +14256,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -14305,7 +14339,6 @@ "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -14885,6 +14918,7 @@ "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -14899,6 +14933,7 @@ "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -14948,6 +14983,7 @@ "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10.13.0" } diff --git a/src/breakpoints-legacy.ts b/src/breakpoints-legacy.ts index 8d368385..b0a9ba57 100644 --- a/src/breakpoints-legacy.ts +++ b/src/breakpoints-legacy.ts @@ -69,7 +69,7 @@ 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: BreakpointsMessage = { - type: OutgoingEventType.breakpoints, + type: OutgoingEventType.Breakpoints, breakpoints: { path: generatedRemoteLocalPath, breakpoints: generatedBreakpoints.length ? generatedBreakpoints : undefined, diff --git a/src/protocol-events.ts b/src/protocol-events.ts index 7b878ca7..2b44972c 100644 --- a/src/protocol-events.ts +++ b/src/protocol-events.ts @@ -31,17 +31,17 @@ export const DEBUGGER_PROTOCOL_VERSION = ProtocolVersion.SupportCerealSerializat // ------------------------------------------------------------------------- // Interfaces for event message payloads (received from the debugee) // ------------------------------------------------------------------------- -export const IncomingEventType = { - stopped: 'StoppedEvent', - thread: 'ThreadEvent', - print: 'PrintEvent', - notification: 'NotificationEvent', - protocol: 'ProtocolEvent', - stat2: 'StatEvent2', - schema: 'SchemaEvent', - profilerCapture: 'ProfilerCapture', - debuggeeResponse: 'debuggee-response', -} as const; +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; @@ -49,125 +49,129 @@ export interface PluginDetails { } export interface ProtocolCapabilities { - type: typeof IncomingEventType.protocol; + type: IncomingEventType.Protocol; version: number; plugins: PluginDetails[]; require_passcode?: boolean; } export interface ProfilerCapture { - type: typeof IncomingEventType.profilerCapture; + type: IncomingEventType.ProfilerCapture; capture_base_path: string; capture_data: string; } export interface StoppedEventMessage { - type: typeof IncomingEventType.stopped; + type: IncomingEventType.Stopped; reason: string; thread: number; } export interface ThreadEventMessage { - type: typeof IncomingEventType.thread; + type: IncomingEventType.Thread; reason: string; thread: number; } export interface PrintEventMessage { - type: typeof IncomingEventType.print; + type: IncomingEventType.Print; message: string; logLevel: LogLevel; } export interface NotificationEventMessage { - type: typeof IncomingEventType.notification; + type: IncomingEventType.Notification; message: string; logLevel: LogLevel; } export interface DebuggeeResponseEnvelope { - type: typeof IncomingEventType.debuggeeResponse; + type: IncomingEventType.DebuggeeResponse; request_seq: number; args?: unknown; success?: boolean; response_message?: string; } +type StatEventMessage = StatMessageModel & { + type: IncomingEventType.Stat2; +}; + export type IncomingDebuggeeMessage = - | PluginDetails | ProtocolCapabilities | ProfilerCapture | StoppedEventMessage | ThreadEventMessage | PrintEventMessage | NotificationEventMessage + | StatEventMessage | DebuggeeResponseEnvelope; // ------------------------------------------------------------------------- // Interfaces for outbound message payloads (sent to the debugee) // ------------------------------------------------------------------------- -export const OutgoingEventType = { - protocol: 'protocol', - minecraftCommand: 'minecraftCommand', - startProfiler: 'startProfiler', - stopProfiler: 'stopProfiler', - stopOnException: 'stopOnException', - resume: 'resume', - request: 'request', - breakpoints: 'breakpoints', - debuggerRequest: 'debugger-request' -} as const; +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: typeof OutgoingEventType.protocol; + type: OutgoingEventType.Protocol; version: number; target_module_uuid?: string; passcode?: string; } export interface MinecraftCommandLegacyMessage { - type: typeof OutgoingEventType.minecraftCommand; + type: OutgoingEventType.MinecraftCommand; command: string; dimension_type: string; } export interface MinecraftCommandMessage { - type: typeof OutgoingEventType.minecraftCommand; + type: OutgoingEventType.MinecraftCommand; command: { command: string; dimension_type: string }; } export interface StartProfilerMessage { - type: typeof OutgoingEventType.startProfiler; + type: OutgoingEventType.StartProfiler; profiler: { target_module_uuid?: string }; } export interface StopProfilerMessage { - type: typeof OutgoingEventType.stopProfiler; + type: OutgoingEventType.StopProfiler; profiler: { captures_path: string; target_module_uuid?: string }; } export interface StopOnExceptionMessage { - type: typeof OutgoingEventType.stopOnException; + type: OutgoingEventType.StopOnException; stopOnException: boolean; } export interface ResumeMessage { - type: typeof OutgoingEventType.resume; + type: OutgoingEventType.Resume; } export interface RequestMessage { - type: typeof OutgoingEventType.request; + type: OutgoingEventType.Request; request: { request_seq: number; command: string; args: unknown }; } export interface BreakpointsMessage { - type: typeof OutgoingEventType.breakpoints; + type: OutgoingEventType.Breakpoints; breakpoints: { path: string; breakpoints: DebugProtocol.SourceBreakpoint[] | undefined }; } export interface DebuggerRequestEnvelope { - type: typeof OutgoingEventType.debuggerRequest; + type: OutgoingEventType.DebuggerRequest; request: { request_seq: number; request: string; @@ -196,17 +200,20 @@ export type { StatMessageModel }; // Registry that maps event type name strings to handler callbacks // ------------------------------------------------------------------------- -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type DebuggeeEventHandler = (event: any) => void; +type DebuggeeEventHandler = ( + event: Extract, +) => void; export class DebuggeeEventRegistry { - private readonly _handlers = new Map(); + private readonly _handlers = new Map void>(); - public register(eventType: string, handler: DebuggeeEventHandler): void { - this._handlers.set(eventType, handler); + public register(eventType: T, handler: DebuggeeEventHandler): void { + this._handlers.set(eventType, event => { + handler(event as Extract); + }); } - public dispatch(eventMessage: { type: string }): boolean { + public dispatch(eventMessage: IncomingDebuggeeMessage): boolean { const handler = this._handlers.get(eventMessage.type); if (handler) { handler(eventMessage); diff --git a/src/requests/request-manager.test.ts b/src/requests/request-manager.test.ts index 0bfd83c0..fcc81fd3 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; @@ -27,7 +28,7 @@ describe('RequestManager', () => { }); 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 }, @@ -78,7 +79,7 @@ describe('RequestManager', () => { }); 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, }); diff --git a/src/requests/request-manager.ts b/src/requests/request-manager.ts index 53225427..cc59dd5e 100644 --- a/src/requests/request-manager.ts +++ b/src/requests/request-manager.ts @@ -47,7 +47,7 @@ export class RequestManager { // Create an envelope to hold the request, and send it to the debuggee const envelope: DebuggerRequestEnvelope = { - type: OutgoingEventType.debuggerRequest, + type: OutgoingEventType.DebuggerRequest, request: { request_seq: seq, request, diff --git a/src/session.ts b/src/session.ts index d7ec2cd9..8337f1e5 100644 --- a/src/session.ts +++ b/src/session.ts @@ -178,27 +178,27 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { // 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._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._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._eventRegistry.register(IncomingEventType.Print, (msg: PrintEventMessage) => { this.handlePrintEvent(msg.message, msg.logLevel); }); - this._eventRegistry.register(IncomingEventType.notification, (msg: NotificationEventMessage) => { + this._eventRegistry.register(IncomingEventType.Notification, (msg: NotificationEventMessage) => { this.showNotification(msg.message, msg.logLevel); }); - this._eventRegistry.register(IncomingEventType.protocol, (msg: ProtocolCapabilities) => { + this._eventRegistry.register(IncomingEventType.Protocol, (msg: ProtocolCapabilities) => { this.handleProtocolEvent(msg); }); - this._eventRegistry.register(IncomingEventType.stat2, (msg: StatMessageModel) => { + this._eventRegistry.register(IncomingEventType.Stat2, (msg: StatMessageModel) => { this._statsProvider.setStats(msg); }); - this._eventRegistry.register(IncomingEventType.profilerCapture, (msg: ProfilerCapture) => { + this._eventRegistry.register(IncomingEventType.ProfilerCapture, (msg: ProfilerCapture) => { this.handleProfilerCapture(msg); }); } @@ -222,13 +222,13 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { private onRunMinecraftCommand(command: string): void { if (this._clientProtocolVersion < ProtocolVersion.SupportProfilerCaptures) { this.sendDebuggeeMessage({ - type: OutgoingEventType.minecraftCommand, + type: OutgoingEventType.MinecraftCommand, command: command, dimension_type: 'overworld', }); } else { this.sendDebuggeeMessage({ - type: OutgoingEventType.minecraftCommand, + type: OutgoingEventType.MinecraftCommand, command: { command: command, dimension_type: 'overworld', @@ -239,7 +239,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { private onStartProfiler(): void { this.sendDebuggeeMessage({ - type: OutgoingEventType.startProfiler, + type: OutgoingEventType.StartProfiler, profiler: { target_module_uuid: this._targetModuleUuid, }, @@ -248,7 +248,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { private onStopProfiler(capturesBasePath: string): void { this.sendDebuggeeMessage({ - type: OutgoingEventType.stopProfiler, + type: OutgoingEventType.StopProfiler, profiler: { captures_path: capturesBasePath, target_module_uuid: this._targetModuleUuid, @@ -449,7 +449,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { args: DebugProtocol.SetExceptionBreakpointsArguments, ): void { this.sendDebuggeeMessage({ - type: OutgoingEventType.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 }); @@ -458,7 +458,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse): void { this.sendDebuggeeMessage({ - type: OutgoingEventType.resume, + type: OutgoingEventType.Resume, }); this.sendResponse(response); @@ -775,7 +775,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { // respond with protocol version and chosen debugee target this.sendDebuggeeMessage({ - type: OutgoingEventType.protocol, + type: OutgoingEventType.Protocol, version: protocolVersion, target_module_uuid: targetModuleUuid, passcode: passcode, @@ -894,7 +894,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { private makeRequestPayload(requestSeq: number, responseCommand: string, args: unknown): RequestMessage { const envelope: RequestMessage = { - type: OutgoingEventType.request, + type: OutgoingEventType.Request, request: { request_seq: requestSeq, command: responseCommand, @@ -927,7 +927,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { private receiveDebugeeMessage(envelope: any) { if (envelope.type === 'event') { this.handleDebugeeEvent(envelope.event); - } else if (envelope.type === IncomingEventType.debuggeeResponse) { + } else if (envelope.type === IncomingEventType.DebuggeeResponse) { if (!this._minecraftCapabilities.supportsDebuggerRequests) { this.log( 'Received debuggee-response from a Minecraft instance that should not support it.', diff --git a/src/stats/stats-provider.ts b/src/stats/stats-provider.ts index ddace2ff..48c53f80 100644 --- a/src/stats/stats-provider.ts +++ b/src/stats/stats-provider.ts @@ -46,7 +46,7 @@ export class StatsProvider { this._fireStatUpdated(stat, stats.tick); } } - + public start(): void { throw new Error('Method not implemented.'); } From 24f294eff4b6fe9792ab384e63c0db41c31a78f0 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Wed, 27 May 2026 17:09:05 -0700 Subject: [PATCH 10/16] Update protocol-events.ts --- src/protocol-events.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/protocol-events.ts b/src/protocol-events.ts index 2b44972c..54fd7f50 100644 --- a/src/protocol-events.ts +++ b/src/protocol-events.ts @@ -93,7 +93,7 @@ export interface DebuggeeResponseEnvelope { response_message?: string; } -type StatEventMessage = StatMessageModel & { +export type StatEventMessage = StatMessageModel & { type: IncomingEventType.Stat2; }; @@ -193,9 +193,6 @@ export type OutgoingDebuggeeMessage = -// Re-export for callers that need these alongside event types -export type { StatMessageModel }; - // ------------------------------------------------------------------------- // Registry that maps event type name strings to handler callbacks // ------------------------------------------------------------------------- From 4f7416d9543c267cc34cd95e6d4a2a62f619ca46 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Wed, 27 May 2026 17:12:00 -0700 Subject: [PATCH 11/16] Update package-lock.json --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index d3527aeb..17d04ca8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15326,4 +15326,4 @@ } } } -} +} \ No newline at end of file From a9ea44a7c3a27e567db43392296b665bfa262073 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Wed, 27 May 2026 17:13:21 -0700 Subject: [PATCH 12/16] Update package-lock.json --- webview-ui/package-lock.json | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index d0dce43b..9891a45a 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -69,7 +69,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", "dev": true, - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -1527,7 +1526,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.20.0" } @@ -1632,7 +1630,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001629", "electron-to-chromium": "^1.4.796", @@ -2008,7 +2005,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "peer": true, "engines": { "node": ">=12" } @@ -2436,7 +2432,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -2767,7 +2762,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", "dev": true, - "peer": true, "requires": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", @@ -3714,7 +3708,6 @@ "version": "22.13.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", - "peer": true, "requires": { "undici-types": "~6.20.0" } @@ -3796,7 +3789,6 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", "dev": true, - "peer": true, "requires": { "caniuse-lite": "^1.0.30001629", "electron-to-chromium": "^1.4.796", @@ -4061,8 +4053,7 @@ "d3-selection": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "peer": true + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" }, "d3-shape": { "version": "3.2.0", @@ -4356,7 +4347,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "peer": true, "requires": { "loose-envify": "^1.1.0" } From 97d25d6cc12ce7061215534a589b8ba5c79fb333 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Wed, 27 May 2026 17:14:23 -0700 Subject: [PATCH 13/16] Update package-lock.json --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 17d04ca8..d3527aeb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15326,4 +15326,4 @@ } } } -} \ No newline at end of file +} From b1516ab9546b48fe1124f61375f6ebed27ca0625 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Fri, 29 May 2026 16:05:18 -0700 Subject: [PATCH 14/16] tweaks --- src/breakpoints-legacy.ts | 6 ++---- src/protocol-events.ts | 23 +++++++++++--------- src/requests/request-manager.ts | 8 +++---- src/session.ts | 38 ++++++++++----------------------- 4 files changed, 29 insertions(+), 46 deletions(-) diff --git a/src/breakpoints-legacy.ts b/src/breakpoints-legacy.ts index b0a9ba57..59fb1ef0 100644 --- a/src/breakpoints-legacy.ts +++ b/src/breakpoints-legacy.ts @@ -70,10 +70,8 @@ export class BreakpointsLegacy implements IBreakpointsHandler { for (const [generatedRemoteLocalPath, generatedBreakpoints] of generatedBreakpointsMap) { const envelope: BreakpointsMessage = { type: OutgoingEventType.Breakpoints, - breakpoints: { - path: generatedRemoteLocalPath, - breakpoints: generatedBreakpoints.length ? generatedBreakpoints : undefined, - }, + path: generatedRemoteLocalPath, + breakpoints: generatedBreakpoints.length ? generatedBreakpoints : undefined, }; this._messageSender.sendDebuggeeMessage(envelope); } diff --git a/src/protocol-events.ts b/src/protocol-events.ts index 54fd7f50..d5db206e 100644 --- a/src/protocol-events.ts +++ b/src/protocol-events.ts @@ -138,17 +138,19 @@ export interface MinecraftCommandLegacyMessage { export interface MinecraftCommandMessage { type: OutgoingEventType.MinecraftCommand; - command: { command: string; dimension_type: string }; + command: string; + dimension_type: string; } export interface StartProfilerMessage { type: OutgoingEventType.StartProfiler; - profiler: { target_module_uuid?: string }; + target_module_uuid?: string; } export interface StopProfilerMessage { type: OutgoingEventType.StopProfiler; - profiler: { captures_path: string; target_module_uuid?: string }; + captures_path: string; + target_module_uuid?: string; } export interface StopOnExceptionMessage { @@ -162,21 +164,22 @@ export interface ResumeMessage { export interface RequestMessage { type: OutgoingEventType.Request; - request: { request_seq: number; command: string; args: unknown }; + request_seq: number; + command: string; + args: unknown; } export interface BreakpointsMessage { type: OutgoingEventType.Breakpoints; - breakpoints: { path: string; breakpoints: DebugProtocol.SourceBreakpoint[] | undefined }; + path: string; + breakpoints: DebugProtocol.SourceBreakpoint[] | undefined; } export interface DebuggerRequestEnvelope { type: OutgoingEventType.DebuggerRequest; - request: { - request_seq: number; - request: string; - args?: unknown; - }; + request_seq: number; + request: string; + args?: unknown; } export type OutgoingDebuggeeMessage = diff --git a/src/requests/request-manager.ts b/src/requests/request-manager.ts index cc59dd5e..9716a37f 100644 --- a/src/requests/request-manager.ts +++ b/src/requests/request-manager.ts @@ -48,11 +48,9 @@ export class RequestManager { // Create an envelope to hold the request, and send it to the debuggee const envelope: DebuggerRequestEnvelope = { type: OutgoingEventType.DebuggerRequest, - request: { - request_seq: seq, - request, - args, - }, + request_seq: seq, + request, + args, }; this._sender.sendDebuggeeMessage(envelope); diff --git a/src/session.ts b/src/session.ts index 8337f1e5..faffe178 100644 --- a/src/session.ts +++ b/src/session.ts @@ -220,39 +220,25 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { // ------------------------------------------------------------------------ private onRunMinecraftCommand(command: string): void { - if (this._clientProtocolVersion < ProtocolVersion.SupportProfilerCaptures) { - this.sendDebuggeeMessage({ - type: OutgoingEventType.MinecraftCommand, - command: command, - dimension_type: 'overworld', - }); - } else { - this.sendDebuggeeMessage({ - type: OutgoingEventType.MinecraftCommand, - command: { - command: command, - dimension_type: 'overworld', - }, - }); - } + this.sendDebuggeeMessage({ + type: OutgoingEventType.MinecraftCommand, + command: command, + dimension_type: 'overworld', + }); } private onStartProfiler(): void { this.sendDebuggeeMessage({ type: OutgoingEventType.StartProfiler, - profiler: { - target_module_uuid: this._targetModuleUuid, - }, + target_module_uuid: this._targetModuleUuid, }); } private onStopProfiler(capturesBasePath: string): void { this.sendDebuggeeMessage({ type: OutgoingEventType.StopProfiler, - profiler: { - captures_path: capturesBasePath, - target_module_uuid: this._targetModuleUuid, - }, + captures_path: capturesBasePath, + target_module_uuid: this._targetModuleUuid, }); } @@ -895,11 +881,9 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { private makeRequestPayload(requestSeq: number, responseCommand: string, args: unknown): RequestMessage { const envelope: RequestMessage = { type: OutgoingEventType.Request, - request: { - request_seq: requestSeq, - command: responseCommand, - args, - }, + request_seq: requestSeq, + command: responseCommand, + args, }; return envelope; } From 6f2e516889ead9a681f741d4ef2e9f9c88651521 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Fri, 29 May 2026 17:32:33 -0700 Subject: [PATCH 15/16] legacy support --- src/protocol-events.ts | 39 +++++++++++- src/requests/request-manager.test.ts | 10 +-- src/requests/request-manager.ts | 28 ++++++--- src/session.ts | 92 +++++++++++++++++++++------- 4 files changed, 129 insertions(+), 40 deletions(-) diff --git a/src/protocol-events.ts b/src/protocol-events.ts index d5db206e..8ca3fefa 100644 --- a/src/protocol-events.ts +++ b/src/protocol-events.ts @@ -132,21 +132,30 @@ export interface ProtocolResponse { export interface MinecraftCommandLegacyMessage { type: OutgoingEventType.MinecraftCommand; - command: string; - dimension_type: string; + command: { command: string; dimension_type: string }; } export interface MinecraftCommandMessage { type: OutgoingEventType.MinecraftCommand; - command: string; + 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; @@ -162,6 +171,11 @@ 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; @@ -169,12 +183,26 @@ export interface RequestMessage { 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; @@ -186,12 +214,17 @@ export type OutgoingDebuggeeMessage = | ProtocolResponse | MinecraftCommandLegacyMessage | MinecraftCommandMessage + | StartProfilerLegacyMessage | StartProfilerMessage + | StopProfilerLegacyMessage | StopProfilerMessage | StopOnExceptionMessage | ResumeMessage + | RequestLegacyMessage | RequestMessage + | BreakpointsLegacyMessage | BreakpointsMessage + | DebuggerRequestLegacyEnvelope | DebuggerRequestEnvelope; diff --git a/src/requests/request-manager.test.ts b/src/requests/request-manager.test.ts index fcc81fd3..5a6736c2 100644 --- a/src/requests/request-manager.test.ts +++ b/src/requests/request-manager.test.ts @@ -22,7 +22,7 @@ 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' }, }); @@ -57,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( @@ -74,7 +74,7 @@ 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', }); @@ -107,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 9716a37f..6482fa78 100644 --- a/src/requests/request-manager.ts +++ b/src/requests/request-manager.ts @@ -3,7 +3,7 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import { DebuggerRequestArguments} from './debugger-request-schema'; import { IDebuggeeMessageSender } from '../debuggee-message-sender'; -import { DebuggeeResponseEnvelope, DebuggerRequestEnvelope, OutgoingEventType } from '../protocol-events'; +import { DebuggeeResponseEnvelope, DebuggerRequestEnvelope, OutgoingEventType, ProtocolVersion } from '../protocol-events'; interface PendingDebuggerRequest { resolve: (value: DebuggeeResponseEnvelope) => void; @@ -21,6 +21,7 @@ export class RequestManager { } public sendDebuggerRequest( + clientProtocolVersion: number, response: DebugProtocol.Response, debuggerRequestArgs: DebuggerRequestArguments, timeoutMs: number = this._defaultDebuggerRequestTimeoutMs, @@ -46,14 +47,23 @@ export class RequestManager { }); // Create an envelope to hold the request, and send it to the debuggee - const envelope: DebuggerRequestEnvelope = { - type: OutgoingEventType.DebuggerRequest, - request_seq: seq, - request, - args, - }; - - this._sender.sendDebuggeeMessage(envelope); + if (clientProtocolVersion >= ProtocolVersion.SupportCerealSerialization) { + this._sender.sendDebuggeeMessage({ + type: OutgoingEventType.DebuggerRequest, + request_seq: seq, + request, + args, + }); + } else { + this._sender.sendDebuggeeMessage({ + type: OutgoingEventType.DebuggerRequest, + request: { + request_seq: seq, + request, + args, + }, + }); + } }); } diff --git a/src/session.ts b/src/session.ts index faffe178..43fd4048 100644 --- a/src/session.ts +++ b/src/session.ts @@ -57,7 +57,8 @@ import { RequestMessage, StoppedEventMessage, ThreadEventMessage, - DebuggeeResponseEnvelope + DebuggeeResponseEnvelope, + RequestLegacyMessage } from './protocol-events'; import { SourceMaps } from './source-maps'; import { StatMessageModel, StatsProvider } from './stats/stats-provider'; @@ -220,26 +221,57 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { // ------------------------------------------------------------------------ private onRunMinecraftCommand(command: string): void { - this.sendDebuggeeMessage({ - type: OutgoingEventType.MinecraftCommand, - command: command, - dimension_type: 'overworld', - }); + if (this._clientProtocolVersion < ProtocolVersion.SupportProfilerCaptures || + this._clientProtocolVersion >= ProtocolVersion.SupportCerealSerialization) { + this.sendDebuggeeMessage({ + type: OutgoingEventType.MinecraftCommand, + command: command, + dimension_type: 'overworld', + }); + } else { + this.sendDebuggeeMessage({ + type: OutgoingEventType.MinecraftCommand, + command: { + command: command, + dimension_type: 'overworld', + }, + }); + } } private onStartProfiler(): void { - this.sendDebuggeeMessage({ - type: OutgoingEventType.StartProfiler, - target_module_uuid: this._targetModuleUuid, - }); + 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: OutgoingEventType.StopProfiler, - captures_path: capturesBasePath, - target_module_uuid: this._targetModuleUuid, - }); + 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( @@ -659,6 +691,7 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { try { const result = await this._requestManager?.sendDebuggerRequest( + this._clientProtocolVersion, response, args as DebuggerRequestArguments, ); @@ -878,14 +911,27 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { this.sendDebuggeeMessage(this.makeRequestPayload(requestSeq, response.command, args)); } - private makeRequestPayload(requestSeq: number, responseCommand: string, args: unknown): RequestMessage { - const envelope: RequestMessage = { - type: OutgoingEventType.Request, - request_seq: requestSeq, - command: responseCommand, - args, - }; - return envelope; + 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; + } + else { + const envelope: RequestLegacyMessage = { + type: OutgoingEventType.Request, + request: { + request_seq: requestSeq, + command: responseCommand, + args, + }, + }; + return envelope; + } } public sendDebuggeeMessage(envelope: OutgoingDebuggeeMessage): void { From d803ccde41136a4aa34892a4f89577bdff172935 Mon Sep 17 00:00:00 2001 From: Alex Denford SBL Date: Fri, 29 May 2026 17:42:10 -0700 Subject: [PATCH 16/16] Update request-manager.ts --- src/requests/request-manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/requests/request-manager.ts b/src/requests/request-manager.ts index 6482fa78..9d72ddc6 100644 --- a/src/requests/request-manager.ts +++ b/src/requests/request-manager.ts @@ -3,7 +3,7 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import { DebuggerRequestArguments} from './debugger-request-schema'; import { IDebuggeeMessageSender } from '../debuggee-message-sender'; -import { DebuggeeResponseEnvelope, DebuggerRequestEnvelope, OutgoingEventType, ProtocolVersion } from '../protocol-events'; +import { DebuggeeResponseEnvelope, OutgoingEventType, ProtocolVersion } from '../protocol-events'; interface PendingDebuggerRequest { resolve: (value: DebuggeeResponseEnvelope) => void;