diff --git a/icons/button_disabled.png b/icons/button_disabled.png new file mode 100644 index 00000000..c8c64a16 Binary files /dev/null and b/icons/button_disabled.png differ diff --git a/icons/button_enabled.png b/icons/button_enabled.png new file mode 100644 index 00000000..394c01d8 Binary files /dev/null and b/icons/button_enabled.png differ diff --git a/src/extension.ts b/src/extension.ts index 6d442a4f..21c43c59 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -79,7 +79,7 @@ export function activate(context: vscode.ExtensionContext): void { }); const liveDiagnosticsCommand = vscode.commands.registerCommand('minecraft-debugger.liveDiagnostics', () => { - MinecraftDiagnosticsPanel.render(context.extensionUri, liveStatsProvider, eventEmitter); + MinecraftDiagnosticsPanel.render(context.extensionUri, liveStatsProvider, eventEmitter, context.globalState); }); const replayDiagnosticsCommand = vscode.commands.registerCommand( @@ -98,7 +98,7 @@ export function activate(context: vscode.ExtensionContext): void { return; } const replayStats = new ReplayStatsProvider(fileUri[0].fsPath); - MinecraftDiagnosticsPanel.render(context.extensionUri, replayStats, eventEmitter); + MinecraftDiagnosticsPanel.render(context.extensionUri, replayStats, eventEmitter, context.globalState); }, ); diff --git a/src/panels/minecraft-diagnostics.ts b/src/panels/minecraft-diagnostics.ts index b680d19d..eafc0684 100644 --- a/src/panels/minecraft-diagnostics.ts +++ b/src/panels/minecraft-diagnostics.ts @@ -1,6 +1,6 @@ // Copyright (C) Microsoft Corporation. All rights reserved. -import { Disposable, Webview, WebviewPanel, window, workspace, Uri, ViewColumn } from 'vscode'; +import { Disposable, Memento, Webview, WebviewPanel, window, workspace, Uri, ViewColumn } from 'vscode'; import { EventEmitter } from 'stream'; import { getUri } from '../utilities/getUri'; import { getNonce } from '../utilities/getNonce'; @@ -8,6 +8,9 @@ import { DebuggerRequestHandler } from '../requests/debugger-request-handler'; import { StatData, StatsListener, StatsProvider } from '../stats/stats-provider'; import { DiagnosticsTabDescriptor } from '../diagnostics-schema'; +const DIAGNOSTICS_TAB_STATES_KEY = 'minecraftDiagnosticsTabStates'; +type DiagnosticsTabStates = Record; + export class MinecraftDiagnosticsPanel { private static activeDiagnosticsPanels: MinecraftDiagnosticsPanel[] = []; @@ -24,6 +27,7 @@ export class MinecraftDiagnosticsPanel { statsTracker: StatsProvider, eventEmitter: EventEmitter, debuggerRequestHandler: DebuggerRequestHandler, + private readonly _globalState: Memento, ) { this._panel = panel; this._statsTracker = statsTracker; @@ -39,6 +43,7 @@ export class MinecraftDiagnosticsPanel { this._panel.webview, extensionUri, statsTracker.manualControl(), + this.getDiagnosticsTabStates(), ); // Handle events from the webview panel @@ -50,6 +55,7 @@ export class MinecraftDiagnosticsPanel { this._panel.webview, extensionUri, statsTracker.manualControl(), + this.getDiagnosticsTabStates(), ); break; case 'pause': @@ -75,6 +81,12 @@ export class MinecraftDiagnosticsPanel { case 'debugger-request': this._debuggerRequestHandler.handleDebuggerRequest(message.request, message.args); break; + case 'set-diagnostics-active': + this.handleDiagnosticsActiveMessage(message); + break; + case 'sync-diagnostics-tabs': + this._eventEmitter.emit('sync-diagnostics-tabs', message.states); + break; case 'export-data': void this.handleExportDataMessage(message); break; @@ -132,6 +144,39 @@ export class MinecraftDiagnosticsPanel { this._statsTracker.addStatListener(this._statsCallback); } + private getDiagnosticsTabStates(): DiagnosticsTabStates { + const states = this._globalState.get(DIAGNOSTICS_TAB_STATES_KEY, {}); + if (states === null || typeof states !== 'object' || Array.isArray(states)) { + return {}; + } + + return Object.fromEntries( + Object.entries(states).filter(([tabName, active]) => tabName.trim() !== '' && typeof active === 'boolean'), + ); + } + + private handleDiagnosticsActiveMessage(message: any): void { + if ( + typeof message.tabName !== 'string' || + message.tabName.trim() === '' || + !Array.isArray(message.collectorNames) || + message.collectorNames.some((name: unknown) => typeof name !== 'string' || name.trim() === '') || + typeof message.active !== 'boolean' + ) { + return; + } + + const states = this.getDiagnosticsTabStates(); + states[message.tabName] = message.active; + void this._globalState.update(DIAGNOSTICS_TAB_STATES_KEY, states); + this._eventEmitter.emit('set-diagnostics-active', message.collectorNames, message.active); + this._panel.webview.postMessage({ + type: 'diagnostics-tab-state', + tabName: message.tabName, + active: message.active, + }); + } + private async handleExportDataMessage(message: any): Promise { if (typeof message.content !== 'string') { console.error('Received export-data message without a valid content string.'); @@ -161,7 +206,12 @@ export class MinecraftDiagnosticsPanel { window.showInformationMessage(`Exported diagnostics data to ${outputUri.fsPath}.`); } - public static render(extensionUri: Uri, statsTracker: StatsProvider, eventEmitter: EventEmitter): void { + public static render( + extensionUri: Uri, + statsTracker: StatsProvider, + eventEmitter: EventEmitter, + globalState: Memento, + ): void { const statsTrackerId = statsTracker.uniqueId; const existingPanel = MinecraftDiagnosticsPanel.activeDiagnosticsPanels.find( panel => panel._statsTracker.uniqueId === statsTrackerId, @@ -189,6 +239,7 @@ export class MinecraftDiagnosticsPanel { statsTracker, eventEmitter, new DebuggerRequestHandler(panel.webview), + globalState, ), ); } @@ -217,7 +268,12 @@ export class MinecraftDiagnosticsPanel { } } - private _getWebviewContent(webview: Webview, extensionUri: Uri, showReplayControls: boolean) { + private _getWebviewContent( + webview: Webview, + extensionUri: Uri, + showReplayControls: boolean, + diagnosticsTabStates: DiagnosticsTabStates, + ) { // The CSS file from the React build output const stylesUri = getUri(webview, extensionUri, ['webview-ui', 'build', 'assets', 'diagnosticsPanel.css']); // The JS file from the React build output @@ -231,11 +287,11 @@ export class MinecraftDiagnosticsPanel { - + Minecraft Diagnostics diff --git a/src/protocol-events.ts b/src/protocol-events.ts index abba5f76..00b9f8a5 100644 --- a/src/protocol-events.ts +++ b/src/protocol-events.ts @@ -16,6 +16,7 @@ import { DiagnosticsTabDescriptor } from './diagnostics-schema'; // 8 - New serialization tech (use Cereal) // 9 - Added support for MC C++/native driven stat descriptors/schemas for UI display // 10 - Added is_empty_tab to DiagnosticsTabDescriptor +// 11 - Added per-tab diagnostics activation export enum ProtocolVersion { _Unknown = 0, @@ -29,9 +30,10 @@ export enum ProtocolVersion { SupportCerealSerialization = 8, SupportNativeDescriptors = 9, SupportEmptyTabs = 10, + SupportDiagnosticsSetActive = 11, } -export const DEBUGGER_PROTOCOL_VERSION = ProtocolVersion.SupportEmptyTabs; +export const DEBUGGER_PROTOCOL_VERSION = ProtocolVersion.SupportDiagnosticsSetActive; // ------------------------------------------------------------------------- // Interfaces for event message payloads (received from the debugee) @@ -131,7 +133,8 @@ export enum OutgoingEventType { Resume = 'resume', Request = 'request', Breakpoints = 'breakpoints', - DebuggerRequest = 'debugger-request' + DebuggerRequest = 'debugger-request', + DiagnosticsSetActive = 'diagnostics-set-active', } export interface ProtocolResponse { @@ -182,6 +185,11 @@ export interface ResumeMessage { type: OutgoingEventType.Resume; } +export interface DiagnosticsSetActiveMessage { + type: OutgoingEventType.DiagnosticsSetActive; + collector_names: string[]; + active: boolean; +} export interface RequestMessage { type: OutgoingEventType.Request; request: { request_seq: number; command: string; args: unknown }; @@ -224,6 +232,7 @@ export type OutgoingDebuggeeMessage = | StopProfilerMessage | StopOnExceptionMessage | ResumeMessage + | DiagnosticsSetActiveMessage | RequestMessage | BreakpointsLegacyMessage | BreakpointsMessage diff --git a/src/session.ts b/src/session.ts index d06705af..595732cb 100644 --- a/src/session.ts +++ b/src/session.ts @@ -172,6 +172,8 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { 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)); + this._eventEmitter.on('set-diagnostics-active', this.onSetDiagnosticsActive.bind(this)); + this._eventEmitter.on('sync-diagnostics-tabs', this.onSyncDiagnosticsTabs.bind(this)); } // Use this to register new events that are handled from the debugee (Minecraft) @@ -212,6 +214,8 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { this._eventEmitter.removeAllListeners('start-profiler'); this._eventEmitter.removeAllListeners('stop-profiler'); this._eventEmitter.removeAllListeners('request-debugger-status'); + this._eventEmitter.removeAllListeners('set-diagnostics-active'); + this._eventEmitter.removeAllListeners('sync-diagnostics-tabs'); if (this._sourceFileWatcher) { this._sourceFileWatcher.dispose(); @@ -277,6 +281,38 @@ export class Session extends DebugSession implements IDebuggeeMessageSender { } } + private onSetDiagnosticsActive(collectorNames: string[], active: boolean): void { + this.sendDiagnosticsSetActive(collectorNames, active); + } + + private onSyncDiagnosticsTabs(collectorStates: Record): void { + const activeCollectors = Object.entries(collectorStates) + .filter(([, active]) => active) + .map(([collectorName]) => collectorName); + const inactiveCollectors = Object.entries(collectorStates) + .filter(([, active]) => !active) + .map(([collectorName]) => collectorName); + + if (activeCollectors.length > 0) { + this.sendDiagnosticsSetActive(activeCollectors, true); + } + if (inactiveCollectors.length > 0) { + this.sendDiagnosticsSetActive(inactiveCollectors, false); + } + } + + private sendDiagnosticsSetActive(collectorNames: string[], active: boolean): void { + if (this._clientProtocolVersion < ProtocolVersion.SupportDiagnosticsSetActive) { + return; + } + + this.sendDebuggeeMessage({ + type: OutgoingEventType.DiagnosticsSetActive, + collector_names: collectorNames, + active, + }); + } + private writeProfilerCaptureToFile( captureData: string, capturePath: string, diff --git a/webview-ui/src/diagnostics_panel/App.css b/webview-ui/src/diagnostics_panel/App.css index 4818ea50..1f1a737a 100644 --- a/webview-ui/src/diagnostics_panel/App.css +++ b/webview-ui/src/diagnostics_panel/App.css @@ -23,6 +23,11 @@ main { border-right: 1px solid var(--vscode-editorGroup-border); } +.vertical-tab-row { + display: flex; + align-items: stretch; +} + .vertical-tab-item { background: transparent; border: none; @@ -35,6 +40,32 @@ main { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + flex: 1; +} + +.diagnostics-tab-toggle { + border: none; + border-left: 1px solid var(--vscode-editorGroup-border); + background: transparent; + color: var(--vscode-descriptionForeground); + cursor: pointer; + font-size: 10px; + min-width: 34px; +} + +.diagnostics-tab-toggle:hover:not(:disabled) { + background-color: var(--vscode-list-hoverBackground); + color: var(--vscode-list-hoverForeground); +} + +.diagnostics-tab-toggle:disabled { + cursor: wait; + opacity: 0.7; +} + +.diagnostics-tab-disabled { + color: var(--vscode-descriptionForeground); + padding: 24px; } .vertical-tab-item:hover { @@ -42,6 +73,14 @@ main { color: var(--vscode-list-hoverForeground); } +.vertical-tab-item.disabled { + color: var(--vscode-descriptionForeground); +} + +.vertical-tab-item.disabled:hover { + color: var(--vscode-descriptionForeground); +} + .vertical-tab-item.active { background-color: var(--vscode-list-activeSelectionBackground); color: var(--vscode-list-activeSelectionForeground); diff --git a/webview-ui/src/diagnostics_panel/App.tsx b/webview-ui/src/diagnostics_panel/App.tsx index eaa23ac4..20714555 100644 --- a/webview-ui/src/diagnostics_panel/App.tsx +++ b/webview-ui/src/diagnostics_panel/App.tsx @@ -23,8 +23,8 @@ const sortedTabPrefabs = [...tabPrefabs].sort((a, b) => a.name.localeCompare(b.n // A tab entry is either a hardcoded prefab or a dynamic descriptor received from the game. type MergedTab = - | { kind: 'prefab'; name: string; tab: TabPrefab } - | { kind: 'dynamic'; name: string; descriptor: DiagnosticsTabDescriptor }; + | { kind: 'prefab'; name: string; tab: TabPrefab; collectorNames: string[] } + | { kind: 'dynamic'; name: string; descriptor: DiagnosticsTabDescriptor; collectorNames: string[] }; declare global { interface Window { @@ -63,9 +63,13 @@ const CLIENT_SELECTION_HELP_TOOLTIP = function App() { const [selectedPlugin, setSelectedPlugin] = useState(''); const [selectedClient, setSelectedClient] = useState(''); - const [currentTab, setCurrentTab] = useState('tab-0'); + const [currentTab, setCurrentTab] = useState(sortedTabPrefabs[0]?.name ?? ''); const [paused, setPaused] = useState(true); const [speed, setSpeed] = useState(''); + const [enabledTabs, setEnabledTabs] = useState>( + window.initialParams.diagnosticsTabStates ?? {}, + ); + const [pendingTabs, setPendingTabs] = useState>({}); // Dynamic schema received from the game on connect. Merged into the prefab tab list. const [schema, setSchema] = useState([]); @@ -88,6 +92,13 @@ function App() { handleDebuggerRequestResult(message); } else if (message.type === 'diagnostics-schema') { setSchema(message.schema as DiagnosticsTabDescriptor[]); + } else if (message.type === 'diagnostics-tab-state' && typeof message.tabName === 'string') { + setEnabledTabs(previous => ({ ...previous, [message.tabName]: message.active === true })); + setPendingTabs(previous => { + const next = { ...previous }; + delete next[message.tabName]; + return next; + }); } }; window.addEventListener('message', handleMessage); @@ -103,19 +114,58 @@ function App() { kind: 'prefab' as const, name: tab.name, tab, + collectorNames: (tab.collectors?.map(collector => collector.collectorName).filter(collectorName => collectorName !== undefined) ?? []) })); for (const descriptor of schema) { const existingIndex = merged.findIndex(t => t.name === descriptor.name); if (existingIndex !== -1) { - merged[existingIndex] = { kind: 'dynamic' as const, name: descriptor.name, descriptor }; + merged[existingIndex] = { + kind: 'dynamic' as const, + name: descriptor.name, + descriptor, + collectorNames: [descriptor.stat_group_id], + }; } else { - merged.push({ kind: 'dynamic' as const, name: descriptor.name, descriptor }); + merged.push({ + kind: 'dynamic' as const, + name: descriptor.name, + descriptor, + collectorNames: [descriptor.stat_group_id], + }); } } merged.sort((a, b) => a.name.localeCompare(b.name)); return merged; }, [schema]); + useEffect(() => { + if (!window.initialParams.showReplayControls) { + const states: Record = {}; + for (const tab of mergedTabs) { + for (const collectorName of tab.collectorNames) { + states[collectorName] = states[collectorName] === true || enabledTabs[tab.name] === true; + } + } + vscode.postMessage({ type: 'sync-diagnostics-tabs', states }); + } + }, [mergedTabs]); + + const setTabActive = (tabName: string, active: boolean) => { + if (window.initialParams.showReplayControls || pendingTabs[tabName] !== undefined) { + return; + } + + setPendingTabs(previous => ({ ...previous, [tabName]: active })); + + const tab = mergedTabs.find(candidate => candidate.name === tabName); + vscode.postMessage({ + type: 'set-diagnostics-active', + tabName, + collectorNames: tab?.collectorNames ?? [], + active, + }); + }; + return (
{window.initialParams.showReplayControls && ( @@ -132,73 +182,107 @@ function App() { )}
- {mergedTabs.map((tab, index) => ( - + {mergedTabs.map(tab => ( +
+ + {!window.initialParams.showReplayControls && ( + + )} +
))}
- {mergedTabs.map((tab, index) => ( + {mergedTabs.map(tab => (
- {tab.kind === 'prefab' ? ( - <> - {tab.tab.dataSource === TabPrefabDataSource.Client ? ( - - ) : ( -
- )} - {tab.tab.dataSource === TabPrefabDataSource.ServerScript ? ( - - ) : ( -
- )} - - - ) : (!tab.descriptor.is_empty_tab && ( - <> - {tab.descriptor.data_source === 'client' && ( - - )} - {tab.descriptor.data_source === 'server_script' && ( - + {tab.tab.dataSource === TabPrefabDataSource.Client ? ( + + ) : ( +
+ )} + {tab.tab.dataSource === TabPrefabDataSource.ServerScript ? ( + + ) : ( +
+ )} + - )} - - - ))} + + ) : ( + !tab.descriptor.is_empty_tab && ( + <> + {tab.descriptor.data_source === 'client' && ( + + )} + {tab.descriptor.data_source === 'server_script' && ( + + )} + + + ) + ) + ) : ( +
+ Enable this tab to start receiving its diagnostics data. +
+ )}
))}
diff --git a/webview-ui/src/diagnostics_panel/Icons.tsx b/webview-ui/src/diagnostics_panel/Icons.tsx index c92a42ce..1df15409 100644 --- a/webview-ui/src/diagnostics_panel/Icons.tsx +++ b/webview-ui/src/diagnostics_panel/Icons.tsx @@ -1,6 +1,15 @@ // Copyright (C) Microsoft Corporation. All rights reserved. +import buttonDisabled from '../../../icons/button_disabled.png'; +import buttonEnabled from '../../../icons/button_enabled.png'; + export const Icons = { + enabled: ( + Enabled + ), + disabled: ( + Disabled + ), restart: ( boolean; } // Used for things like stacked bar charts @@ -103,17 +118,23 @@ export class MultipleStatisticProvider extends StatisticProvider { } protected _handleEvent(event: StatisticUpdatedMessage) { + const statisticParentId = + typeof this.options.statisticParentId === 'object' && + !(this.options.statisticParentId instanceof RegExp) + ? this.options.statisticParentId.collectorName + : this.options.statisticParentId; + // Check event type if (this.options.statisticIds !== undefined && this.options.statisticIds.indexOf(event.id) === -1) { return; } // Check for wrong group - if (this.options.statisticParentId instanceof RegExp) { - if (!this.options.statisticParentId.test(event.group_full_id)) { + if (statisticParentId instanceof RegExp) { + if (!statisticParentId.test(event.group_full_id)) { return; } - } else if (event.group !== this.options.statisticParentId) { + } else if (event.group !== statisticParentId) { return; } diff --git a/webview-ui/src/diagnostics_panel/prefabs/StatisticPrefab.ts b/webview-ui/src/diagnostics_panel/prefabs/StatisticPrefab.ts index bfb7af38..8e4ea09a 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/StatisticPrefab.ts +++ b/webview-ui/src/diagnostics_panel/prefabs/StatisticPrefab.ts @@ -4,5 +4,6 @@ import { ReactNode } from 'react'; export interface StatisticPrefab { name: string; + collectorName: string; reactNode: ReactNode; } diff --git a/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts b/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts index e8cd3c3a..7b0ea092 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts +++ b/webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts @@ -1,3 +1,5 @@ +import type { StatisticPrefab } from './StatisticPrefab'; + export type TabPrefabParams = { selectedClient: string; selectedPlugin: string; @@ -13,5 +15,6 @@ export enum TabPrefabDataSource { export interface TabPrefab { name: string; dataSource: TabPrefabDataSource; + collectors?: StatisticPrefab[]; content: (params: TabPrefabParams) => JSX.Element; } diff --git a/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerBandwidth.tsx b/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerBandwidth.tsx index 749087e8..34717b93 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerBandwidth.tsx +++ b/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerBandwidth.tsx @@ -5,8 +5,11 @@ import { StatisticType, YAxisType } from '../../StatisticResolver'; import { TabPrefab, TabPrefabDataSource } from '../TabPrefab'; import { generateRowsFromStatsPrefabs } from '../utilities'; +const PACKETS_COLLECTOR = 'packets'; + const packetDataReceived: StatisticPrefab = { name: 'Packet Data Received', + collectorName: PACKETS_COLLECTOR, reactNode: ( { return generateRowsFromStatsPrefabs([[packetDataReceived, packetDataSent]]); }, diff --git a/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerMemory.tsx b/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerMemory.tsx index 668c13cd..3166451e 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerMemory.tsx +++ b/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerMemory.tsx @@ -5,14 +5,18 @@ import { StatisticType, YAxisType } from '../../StatisticResolver'; import { TabPrefab, TabPrefabDataSource } from '../TabPrefab'; import { generateRowsFromStatsPrefabs } from '../utilities'; +const APP_MEMORY_COLLECTOR = 'app_memory'; +const RUNTIME_MEMORY_COLLECTOR = 'runtime_memory'; + const AppMemoryUsage: StatisticPrefab = { name: 'App Memory Usage', + collectorName: APP_MEMORY_COLLECTOR, reactNode: ( { return generateRowsFromStatsPrefabs([ [AppMemoryUsage, AppMemoryFree], diff --git a/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerPackets.tsx b/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerPackets.tsx index 2ca7b541..eace06d0 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerPackets.tsx +++ b/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerPackets.tsx @@ -6,8 +6,13 @@ import { TabPrefab, TabPrefabDataSource } from '../TabPrefab'; import { generateRowsFromStatsPrefabs } from '../utilities'; import MinecraftStatisticStackedBarChart from '../../controls/MinecraftStatisticStackedBarChart'; +const PACKETS_COLLECTOR = 'packets'; +const NETWORKING_PACKETS_DETAILS_COLLECTOR = 'networking_packets_details'; +const NETWORKING_PACKETS_DETAILS_PATTERN = new RegExp(`${NETWORKING_PACKETS_DETAILS_COLLECTOR}_.*`); + const packetsReceivedLineChart: StatisticPrefab = { name: 'Packets Received (Line)', + collectorName: PACKETS_COLLECTOR, reactNode: ( { return generateRowsFromStatsPrefabs([ [packetsReceivedLineChart, packetsSentLineChart], diff --git a/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerTiming.tsx b/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerTiming.tsx index db5af90b..1946ede4 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerTiming.tsx +++ b/webview-ui/src/diagnostics_panel/prefabs/tabs/ServerTiming.tsx @@ -5,15 +5,19 @@ import { StatisticType, YAxisType, createStatResolver } from '../../StatisticRes import { TabPrefab, TabPrefabDataSource } from '../TabPrefab'; import { generateRowsFromStatsPrefabs } from '../utilities'; +const SERVER_TICK_TIMINGS_COLLECTOR = 'server_tick_timings'; +const COMMANDS_COLLECTOR = 'commands'; + const ServerTickTimings: StatisticPrefab = { name: 'Server Tick Timings', + collectorName: SERVER_TICK_TIMINGS_COLLECTOR, reactNode: ( { return generateRowsFromStatsPrefabs([[ServerTickTimings], [CommandsRan]]); }, diff --git a/webview-ui/src/diagnostics_panel/prefabs/tabs/World.tsx b/webview-ui/src/diagnostics_panel/prefabs/tabs/World.tsx index f1ba78c1..bea703c9 100644 --- a/webview-ui/src/diagnostics_panel/prefabs/tabs/World.tsx +++ b/webview-ui/src/diagnostics_panel/prefabs/tabs/World.tsx @@ -2,17 +2,23 @@ import MinecraftStatisticLineChart from '../../controls/MinecraftStatisticLineCh import MinecraftStatisticStackedLineChart from '../../controls/MinecraftStatisticStackedLineChart'; import { StatisticPrefab } from '../StatisticPrefab'; import { SimpleStatisticProvider, NestedStatisticProvider } from '../../StatisticProvider'; -import { StatisticType, YAxisType, NestedStatResolver, createStatResolver } from '../../StatisticResolver'; +import { StatisticType, YAxisStyle, YAxisType, NestedStatResolver, createStatResolver } from '../../StatisticResolver'; import { TabPrefab, TabPrefabDataSource } from '../TabPrefab'; import { generateRowsFromStatsPrefabs } from '../utilities'; +const ENTITIES_COLLECTOR = 'entities'; +const CHUNKS_COLLECTOR = 'chunks'; + const entityCount: StatisticPrefab = { name: 'Entity Count', + collectorName: ENTITIES_COLLECTOR, reactNode: ( { return generateRowsFromStatsPrefabs([[entityCount], [loadedChunks]]); }, diff --git a/webview-ui/vite.config.ts b/webview-ui/vite.config.ts index cd40268e..9e79155b 100644 --- a/webview-ui/vite.config.ts +++ b/webview-ui/vite.config.ts @@ -4,6 +4,9 @@ import react from '@vitejs/plugin-react'; // https://vitejs.dev/config/ export default defineConfig({ + // VS Code webviews load the entry script through an asWebviewUri, so asset + // URLs must be resolved relative to that script rather than from `/`. + base: './', plugins: [react()], build: { outDir: 'build',