Skip to content

Commit f4d95a1

Browse files
feat: Added commands and payload size to editor network stats (#354)
This PR adds support for showing action buttons under `MinecraftMultiColumnStatisticTable` header and support for running Minecraft commands from `diagnostics_panel`. Also added actions to control Editor network stats view and columns to display Sent/Received payload total sizes. <img width="1543" height="272" alt="image" src="https://github.com/user-attachments/assets/58481f38-a760-4447-93b0-288c3cba6629" />
1 parent 038a77a commit f4d95a1

7 files changed

Lines changed: 98 additions & 33 deletions

File tree

src/extension.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export function activate(context: vscode.ExtensionContext): void {
7979
});
8080

8181
const liveDiagnosticsCommand = vscode.commands.registerCommand('minecraft-debugger.liveDiagnostics', () => {
82-
MinecraftDiagnosticsPanel.render(context.extensionUri, liveStatsProvider);
82+
MinecraftDiagnosticsPanel.render(context.extensionUri, liveStatsProvider, eventEmitter);
8383
});
8484

8585
const replayDiagnosticsCommand = vscode.commands.registerCommand(
@@ -98,8 +98,8 @@ export function activate(context: vscode.ExtensionContext): void {
9898
return;
9999
}
100100
const replayStats = new ReplayStatsProvider(fileUri[0].fsPath);
101-
MinecraftDiagnosticsPanel.render(context.extensionUri, replayStats);
102-
}
101+
MinecraftDiagnosticsPanel.render(context.extensionUri, replayStats, eventEmitter);
102+
},
103103
);
104104

105105
// Add commands to the extension context

src/panels/minecraft-diagnostics.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (C) Microsoft Corporation. All rights reserved.
22

33
import { Disposable, Webview, WebviewPanel, window, Uri, ViewColumn } from 'vscode';
4+
import { EventEmitter } from 'stream';
45
import { getUri } from '../utilities/getUri';
56
import { getNonce } from '../utilities/getNonce';
67
import { StatData, StatsListener, StatsProvider } from '../stats/stats-provider';
@@ -12,10 +13,17 @@ export class MinecraftDiagnosticsPanel {
1213
private _disposables: Disposable[] = [];
1314
private _statsTracker: StatsProvider;
1415
private _statsCallback: StatsListener | undefined = undefined;
15-
16-
private constructor(panel: WebviewPanel, extensionUri: Uri, statsTracker: StatsProvider) {
16+
private _eventEmitter: EventEmitter;
17+
18+
private constructor(
19+
panel: WebviewPanel,
20+
extensionUri: Uri,
21+
statsTracker: StatsProvider,
22+
eventEmitter: EventEmitter,
23+
) {
1724
this._panel = panel;
1825
this._statsTracker = statsTracker;
26+
this._eventEmitter = eventEmitter;
1927

2028
// Set an event listener to listen for when the panel is disposed (i.e. when the user closes
2129
// the panel or when the panel is closed programmatically)
@@ -54,6 +62,11 @@ export class MinecraftDiagnosticsPanel {
5462
case 'speed':
5563
this._statsTracker.setSpeed(message.speed);
5664
break;
65+
case 'run-minecraft-command':
66+
if (message.command && message.command.trim() !== '') {
67+
this._eventEmitter.emit('run-minecraft-command', message.command);
68+
}
69+
break;
5770
default:
5871
console.error('Unknown message type:', message.type);
5972
break;
@@ -101,7 +114,7 @@ export class MinecraftDiagnosticsPanel {
101114
this._statsTracker.addStatListener(this._statsCallback);
102115
}
103116

104-
public static render(extensionUri: Uri, statsTracker: StatsProvider): void {
117+
public static render(extensionUri: Uri, statsTracker: StatsProvider, eventEmitter: EventEmitter): void {
105118
const statsTrackerId = statsTracker.uniqueId;
106119
const existingPanel = MinecraftDiagnosticsPanel.activeDiagnosticsPanels.find(
107120
panel => panel._statsTracker.uniqueId === statsTrackerId
@@ -123,7 +136,7 @@ export class MinecraftDiagnosticsPanel {
123136
}
124137
);
125138
MinecraftDiagnosticsPanel.activeDiagnosticsPanels.push(
126-
new MinecraftDiagnosticsPanel(panel, extensionUri, statsTracker)
139+
new MinecraftDiagnosticsPanel(panel, extensionUri, statsTracker, eventEmitter),
127140
);
128141
}
129142
}

webview-ui/src/diagnostics_panel/App.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ main {
8787
fill: var(--vscode-editor-background);
8888
}
8989

90+
.minecraft-statistic-table-actions {
91+
flex-direction: row;
92+
display: flex;
93+
width: 100%;
94+
margin-bottom: 10px;
95+
gap: 5px;
96+
}
97+
9098
.minecraft-statistic-table-container {
9199
flex-direction: row;
92100
display: flex;

webview-ui/src/diagnostics_panel/App.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ const onResume = () => {
4747
vscode.postMessage({ type: 'resume' });
4848
};
4949

50+
const onRunCommand = (command: string) => {
51+
vscode.postMessage({ type: 'run-minecraft-command', command: command });
52+
};
53+
5054
function App() {
5155
const [selectedPlugin, setSelectedPlugin] = useState<string>('');
5256
const [selectedClient, setSelectedClient] = useState<string>('');
@@ -122,7 +126,7 @@ function App() {
122126
) : (
123127
<div />
124128
)}
125-
{tabPrefab.content({ selectedClient, selectedPlugin })}
129+
{tabPrefab.content({ selectedClient, selectedPlugin, onRunCommand })}
126130
</VSCodePanelView>
127131
))}
128132
</VSCodePanels>

webview-ui/src/diagnostics_panel/controls/MinecraftMultiColumnStatisticTable.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
VSCodeDataGridCell,
1010
VSCodeDropdown,
1111
VSCodeOption,
12+
VSCodeButton,
1213
} from '@vscode/webview-ui-toolkit/react';
1314

1415
export enum MinecraftMultiColumnStatisticTableSortOrder {
@@ -44,6 +45,7 @@ type MinecraftMultiColumnStatisticTableProps = {
4445
valueLabels: string[]; // Array of labels for value columns
4546
prettifyNames?: boolean; // Whether to format packet names (camelCase -> Camel Case) or keep original format
4647
columnWidths?: string[]; // Optional array of column widths (first is key column, rest are value columns)
48+
actions?: { label: string; onClick: () => void }[]; // Optional actions with labels and commands to run on click
4749
};
4850

4951
const sortOrderOptions = [
@@ -113,6 +115,7 @@ export default function MinecraftMultiColumnStatisticTable({
113115
valueLabels,
114116
prettifyNames = true, // Default to prettifying names for backward compatibility
115117
columnWidths,
118+
actions,
116119
}: MinecraftMultiColumnStatisticTableProps): JSX.Element {
117120
// states
118121
const [data, setData] = useState<MultiColumnTrackedStat[]>([]);
@@ -304,6 +307,15 @@ export default function MinecraftMultiColumnStatisticTable({
304307
return (
305308
<div>
306309
<h2>{title}</h2>
310+
{actions?.length && (
311+
<div className="minecraft-statistic-table-actions">
312+
{actions.map(action => (
313+
<VSCodeButton key={action.label} onClick={action.onClick}>
314+
{action.label}
315+
</VSCodeButton>
316+
))}
317+
</div>
318+
)}
307319
<div className="minecraft-statistic-table-container">
308320
<div className="minecraft-statistic-table-sort-container">
309321
<label htmlFor="sort-order">Sort Order</label>

webview-ui/src/diagnostics_panel/prefabs/TabPrefab.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { StatisticPrefab } from './StatisticPrefab';
33
export type TabPrefabParams = {
44
selectedClient: string;
55
selectedPlugin: string;
6+
onRunCommand: (command: string) => void;
67
};
78

89
export enum TabPrefabDataSource {
Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,63 @@
1+
import { useMemo } from 'react';
12
import { MultipleStatisticProvider } from '../../StatisticProvider';
23
import { TabPrefab, TabPrefabDataSource } from '../TabPrefab';
34
import MinecraftMultiColumnStatisticTable from '../../controls/MinecraftMultiColumnStatisticTable';
45
import { createStatResolver, StatisticType, YAxisType } from '../../StatisticResolver';
56

7+
function EditorNetworkStatsContent(props: { onRunCommand: (command: string) => void }) {
8+
const actions = useMemo(
9+
() => [
10+
{ label: 'Reset', onClick: () => props.onRunCommand(getPayloadMetricsCommandStr('clear')) },
11+
{ label: 'Pause', onClick: () => props.onRunCommand(getPayloadMetricsCommandStr('pause')) },
12+
{
13+
label: 'Resume',
14+
onClick: () => props.onRunCommand(getPayloadMetricsCommandStr('resume')),
15+
},
16+
],
17+
[props.onRunCommand],
18+
);
19+
20+
return (
21+
<div>
22+
<MinecraftMultiColumnStatisticTable
23+
title="Editor Network Packet Statistics"
24+
statisticDataProvider={
25+
new MultipleStatisticProvider({
26+
statisticParentId: 'editor_network_stats',
27+
statisticIds: ['consolidated_data'],
28+
})
29+
}
30+
statisticResolver={createStatResolver({
31+
type: StatisticType.Absolute,
32+
yAxisType: YAxisType.Absolute,
33+
tickRange: 20 * 15, // About 15 seconds
34+
})}
35+
actions={actions}
36+
keyLabel="Packet Type"
37+
valueLabels={[
38+
'Sent Count',
39+
'Received Count',
40+
'Sent Total Size',
41+
'Received Total Size',
42+
'Min Size',
43+
'Max Size',
44+
]}
45+
prettifyNames={false} // Keep original packet name format
46+
defaultSortColumn="value_0" // Sort by "Sent Count" column by default
47+
columnWidths={['400px', '80px', '80px', '80px', '80px', '80px', '80px']} // Custom column widths
48+
/>
49+
</div>
50+
);
51+
}
52+
53+
function getPayloadMetricsCommandStr(command: 'clear' | 'pause' | 'resume'): string {
54+
return `/editorservertest payloadmetrics ${command}`;
55+
}
56+
657
const statsTab: TabPrefab = {
758
name: 'Editor Network Stats',
859
dataSource: TabPrefabDataSource.Server,
9-
content: () => {
10-
return (
11-
<div>
12-
<MinecraftMultiColumnStatisticTable
13-
title="Editor Network Packet Statistics"
14-
statisticDataProvider={
15-
new MultipleStatisticProvider({
16-
statisticParentId: 'editor_network_stats',
17-
statisticIds: ['consolidated_data'],
18-
})
19-
}
20-
statisticResolver={createStatResolver({
21-
type: StatisticType.Absolute,
22-
yAxisType: YAxisType.Absolute,
23-
tickRange: 20 * 15, // About 15 seconds
24-
})}
25-
keyLabel="Packet Type"
26-
valueLabels={['Sent Count', 'Received Count', 'Min Size', 'Max Size']}
27-
prettifyNames={false} // Keep original packet name format
28-
defaultSortColumn="value_0" // Sort by "Sent Count" column by default
29-
columnWidths={['400px', '80px', '80px', '80px', '80px']} // Custom column widths
30-
/>
31-
</div>
32-
);
33-
},
60+
content: props => <EditorNetworkStatsContent onRunCommand={props.onRunCommand} />,
3461
};
3562

3663
export default statsTab;

0 commit comments

Comments
 (0)