Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,17 @@ Some examples of this are:

To see the current list of supported types, see [CI.yml](./.github/workflows/ci.yml#L13)'s 'Lint PR Title' task.

# Tests

# Build
To build and use this extension locally, run the following commands:
```
cd webview-ui
npm install
```
Go to the Run and Debug panel (Ctrl-Shift-D) and select "Run Extension". This will automatically build and run the extension.
You can now use the extension as normal (in the new VS Code instance) by attaching to Minecraft to debug.

# Tests
Create tests using vitest.
Follow the convention `sourcefilename.test.ts` when adding new files.

Expand Down
11 changes: 5 additions & 6 deletions src/breakpoints-legacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -67,12 +68,10 @@ export class BreakpointsLegacy implements IBreakpointsHandler {

// send full set of breakpoints for each generated file, a message per file
for (const [generatedRemoteLocalPath, generatedBreakpoints] of generatedBreakpointsMap) {
const envelope = {
type: 'breakpoints',
breakpoints: {
path: generatedRemoteLocalPath,
breakpoints: generatedBreakpoints.length ? generatedBreakpoints : undefined,
},
const envelope: BreakpointsMessage = {
type: OutgoingEventType.Breakpoints,
path: generatedRemoteLocalPath,
breakpoints: generatedBreakpoints.length ? generatedBreakpoints : undefined,
};
this._messageSender.sendDebuggeeMessage(envelope);
}
Expand Down
3 changes: 2 additions & 1 deletion src/debuggee-message-sender.ts
Original file line number Diff line number Diff line change
@@ -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<unknown>;
}
257 changes: 257 additions & 0 deletions src/protocol-events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
// Copyright (C) Microsoft Corporation. All rights reserved.

import { LogLevel } from '@vscode/debugadapter/lib/logger';
import { DebugProtocol } from '@vscode/debugprotocol';
import { StatMessageModel } from './stats/stats-provider';

// protocol version history
// 1 - initial version
// 2 - add targetModuleUuid to protocol event
// 3 - add array of plugins and target module ids to incoming protocol event
// 4 - mc can require a passcode to connect
// 5 - debugger can take mc script profiler captures
// 6 - breakpoints as request, MC can reject
// 7 - support for debugger requests, MC can reject or respond with args
// 8 - New serialization tech (use Cereal)

export enum ProtocolVersion {
_Unknown = 0,
Initial = 1,
SupportTargetModuleUuid = 2,
SupportTargetSelection = 3,
SupportPasscode = 4,
SupportProfilerCaptures = 5,
SupportBreakpointsAsRequest = 6,
SupportDebuggerRequests = 7,
SupportCerealSerialization = 8,
}

export const DEBUGGER_PROTOCOL_VERSION = ProtocolVersion.SupportCerealSerialization;

// -------------------------------------------------------------------------
// Interfaces for event message payloads (received from the debugee)
// -------------------------------------------------------------------------
export enum IncomingEventType {
Stopped = 'StoppedEvent',
Thread = 'ThreadEvent',
Print = 'PrintEvent',
Notification = 'NotificationEvent',
Protocol = 'ProtocolEvent',
Stat2 = 'StatEvent2',
Schema = 'SchemaEvent',
ProfilerCapture = 'ProfilerCapture',
DebuggeeResponse = 'debuggee-response',
}

export interface PluginDetails {
name: string;
module_uuid: string;
}

export interface ProtocolCapabilities {
type: IncomingEventType.Protocol;
version: number;
plugins: PluginDetails[];
require_passcode?: boolean;
}

export interface ProfilerCapture {
type: IncomingEventType.ProfilerCapture;
capture_base_path: string;
capture_data: string;
}

export interface StoppedEventMessage {
type: IncomingEventType.Stopped;
reason: string;
thread: number;
}

export interface ThreadEventMessage {
type: IncomingEventType.Thread;
reason: string;
thread: number;
}

export interface PrintEventMessage {
type: IncomingEventType.Print;
message: string;
logLevel: LogLevel;
}

export interface NotificationEventMessage {
type: IncomingEventType.Notification;
message: string;
logLevel: LogLevel;
}

export interface DebuggeeResponseEnvelope {
type: IncomingEventType.DebuggeeResponse;
request_seq: number;
args?: unknown;
success?: boolean;
response_message?: string;
}

export type StatEventMessage = StatMessageModel & {
type: IncomingEventType.Stat2;
};

export type IncomingDebuggeeMessage =
| ProtocolCapabilities
| ProfilerCapture
| StoppedEventMessage
| ThreadEventMessage
| PrintEventMessage
| NotificationEventMessage
| StatEventMessage
| DebuggeeResponseEnvelope;


// -------------------------------------------------------------------------
// Interfaces for outbound message payloads (sent to the debugee)
// -------------------------------------------------------------------------
export enum OutgoingEventType {
Protocol = 'protocol',
MinecraftCommand = 'minecraftCommand',
StartProfiler = 'startProfiler',
StopProfiler = 'stopProfiler',
StopOnException = 'stopOnException',
Resume = 'resume',
Request = 'request',
Breakpoints = 'breakpoints',
DebuggerRequest = 'debugger-request'
}

export interface ProtocolResponse {
type: OutgoingEventType.Protocol;
version: number;
target_module_uuid?: string;
passcode?: string;
}

export interface MinecraftCommandLegacyMessage {
type: OutgoingEventType.MinecraftCommand;
command: { command: string; dimension_type: string };
}

export interface MinecraftCommandMessage {
type: OutgoingEventType.MinecraftCommand;
command: string;
dimension_type: string;
}

export interface StartProfilerLegacyMessage {
type: OutgoingEventType.StartProfiler;
profiler: { target_module_uuid?: string };
}

export interface StartProfilerMessage {
type: OutgoingEventType.StartProfiler;
target_module_uuid?: string;
}

export interface StopProfilerLegacyMessage {
type: OutgoingEventType.StopProfiler;
profiler: { captures_path: string; target_module_uuid?: string };
}

export interface StopProfilerMessage {
type: OutgoingEventType.StopProfiler;
captures_path: string;
target_module_uuid?: string;
}

export interface StopOnExceptionMessage {
type: OutgoingEventType.StopOnException;
stopOnException: boolean;
}

export interface ResumeMessage {
type: OutgoingEventType.Resume;
}

export interface RequestLegacyMessage {
type: OutgoingEventType.Request;
request: { request_seq: number; command: string; args: unknown };
}

export interface RequestMessage {
type: OutgoingEventType.Request;
request_seq: number;
command: string;
args: unknown;
}

export interface BreakpointsLegacyMessage {
type: OutgoingEventType.Breakpoints;
breakpoints: { path: string; breakpoints: DebugProtocol.SourceBreakpoint[] | undefined };
}

export interface BreakpointsMessage {
type: OutgoingEventType.Breakpoints;
path: string;
breakpoints: DebugProtocol.SourceBreakpoint[] | undefined;
}

export interface DebuggerRequestLegacyEnvelope {
type: OutgoingEventType.DebuggerRequest;
request: {
request_seq: number;
request: string;
args?: unknown;
};
}

export interface DebuggerRequestEnvelope {
type: OutgoingEventType.DebuggerRequest;
request_seq: number;
request: string;
args?: unknown;
}

export type OutgoingDebuggeeMessage =
| ProtocolResponse
| MinecraftCommandLegacyMessage
| MinecraftCommandMessage
| StartProfilerLegacyMessage
| StartProfilerMessage
| StopProfilerLegacyMessage
| StopProfilerMessage
| StopOnExceptionMessage
| ResumeMessage
| RequestLegacyMessage
| RequestMessage
| BreakpointsLegacyMessage
| BreakpointsMessage
| DebuggerRequestLegacyEnvelope
| DebuggerRequestEnvelope;



// -------------------------------------------------------------------------
// Registry that maps event type name strings to handler callbacks
// -------------------------------------------------------------------------

type DebuggeeEventHandler<T extends IncomingEventType> = (
event: Extract<IncomingDebuggeeMessage, { type: T }>,
) => void;

export class DebuggeeEventRegistry {
private readonly _handlers = new Map<IncomingEventType, (event: IncomingDebuggeeMessage) => void>();

public register<T extends IncomingEventType>(eventType: T, handler: DebuggeeEventHandler<T>): void {
this._handlers.set(eventType, event => {
handler(event as Extract<IncomingDebuggeeMessage, { type: T }>);
});
}

public dispatch(eventMessage: IncomingDebuggeeMessage): boolean {
const handler = this._handlers.get(eventMessage.type);
if (handler) {
handler(eventMessage);
return true;
}
return false;
}
}
21 changes: 1 addition & 20 deletions src/requests/debugger-request-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Loading