Files
shawn 91b878f347 Quaternius weapons, input fixes, sound & path fixes
- Added 6 Quaternius weapon models (Pistol, Revolver, Rifle, Shotgun, P90, SniperRifle)
- Created weapon resource files with balanced stats
- Wired Pistol + Rifle into player.tscn replacing Kenney blasters
- Fixed WASD input (matched input map action names)
- Fixed Escape key toggles mouse capture
- Added Audio autoload singleton
- Fixed broken sound asset paths across all scripts
- Fixed all scene ext_resource text paths (nested under assets/kenney-fps/)
- Fixed all .import source_file paths
- Deleted stale .import files (will be regenerated by editor on open)
2026-07-05 22:53:27 -04:00

248 lines
9.6 KiB
TypeScript

import type { ChildProcess } from 'child_process';
/**
* Thrown when the bridge socket closes (Godot exited, port closed, or peer
* dropped the connection mid-flight). Lets callers distinguish
* "session ended" from generic transport errors.
*/
export declare class BridgeDisconnectedError extends Error {
constructor(message: string);
}
export declare const BRIDGE_WAIT_SPAWNED_TIMEOUT_MS = 8000;
/**
* Normalize a path for cross-platform comparison.
* Folds Windows backslashes to forward slashes and strips trailing slashes,
* so Node's `path.normalize` output matches Godot's `globalize_path("res://")`.
*/
export declare function normalizeForCompare(p: string): string;
/**
* Extract JSON from Godot output by finding the first { or [ and matching to the end.
* This strips debug logs, version banners, and other noise.
*/
export declare function extractJson(output: string): string;
/**
* Strip Godot banner and debug lines from output, keeping only meaningful content.
*/
export declare function cleanOutput(output: string): string;
export declare function cleanStdout(stdout: string): string;
export interface GodotProcess {
process: ChildProcess;
output: string[];
errors: string[];
totalErrorsWritten: number;
exitCode: number | null;
hasExited: boolean;
sessionToken: string;
}
export type RuntimeSessionMode = 'spawned' | 'attached';
export interface RuntimeStopResult {
mode: RuntimeSessionMode;
output: string[];
errors: string[];
externalProcessPreserved?: boolean;
}
export interface GodotServerConfig {
godotPath?: string;
debugMode?: boolean;
}
export interface OperationParams {
[key: string]: unknown;
}
export interface OperationResult {
stdout: string;
stderr: string;
}
export interface ToolAnnotations {
readOnlyHint?: boolean;
destructiveHint?: boolean;
idempotentHint?: boolean;
openWorldHint?: boolean;
title?: string;
}
export interface ToolDefinition {
name: string;
description: string;
inputSchema: {
type: string;
properties: Record<string, unknown>;
required: string[];
};
outputSchema?: {
type: string;
properties?: Record<string, unknown>;
required?: string[];
};
annotations?: ToolAnnotations;
}
export interface ToolResponse {
content: Array<{
type: string;
text?: string;
[k: string]: unknown;
}>;
isError?: boolean;
structuredContent?: Record<string, unknown>;
[k: string]: unknown;
}
export type ToolHandler = (runner: GodotRunner, args: OperationParams) => Promise<ToolResponse> | ToolResponse;
export declare function normalizeParameters(params: OperationParams): OperationParams;
export declare function convertCamelToSnakeCase(params: OperationParams): OperationParams;
/**
* Check whether a display server (X11 / Wayland) is available on the current
* platform. On macOS and Windows the display subsystem is always present;
* on Linux we probe the standard environment variables.
*/
export declare function checkDisplayAvailable(): boolean;
export declare function validatePath(path: string): boolean;
/**
* Stricter check for paths that must stay inside `projectPath`. Rejects `..`
* (via `validatePath`) and absolute paths that escape the project root.
* `path.join('/project', '/etc/passwd')` resolves to `/etc/passwd`, so the
* basic `..`-substring check alone permits absolute-path traversal.
*
* Tolerates a leading `res://` (Godot's project-root URI) by stripping it
* before resolving — autoload entries and resource paths use this prefix.
*/
export declare function validateSubPath(projectPath: string, userPath: string): boolean;
/**
* Validate a Godot scene-tree path (NodePath). Scene-tree paths are a
* separate namespace from filesystem paths — they address nodes inside
* a scene, not files on disk, so the project-root containment check
* in `validateSubPath` does not apply.
*
* Rejects empty strings and `..` segments. Accepts both relative
* (`root/Player`) and absolute (`/root/Player`) Godot forms; the
* codebase convention is the relative form.
*/
export declare function validateNodePath(path: string): boolean;
/**
* True when `child` resolves to `parent` or a path beneath it. Used by
* defense-in-depth checks on bridge-returned paths (e.g. screenshot files
* that must live under `.mcp/screenshots/`).
*/
export declare function isUnderDir(parent: string, child: string): boolean;
/**
* Return `error.message` when `error` is an `Error`, otherwise `'Unknown error'`.
* Centralizes the catch-block boilerplate so handlers can build error responses
* without repeating the `instanceof Error` ternary.
*/
export declare function getErrorMessage(error: unknown): string;
/**
* Build the absolute path to a project's `project.godot` manifest. Use this
* instead of `join(dir, 'project.godot')` ad hoc.
*/
export declare function projectGodotPath(projectDir: string): string;
/**
* Extract the first [ERROR] message from GDScript stderr output.
* Falls back to a generic message if no [ERROR] line is found.
*/
export declare function extractGdError(stderr: string): string;
export declare function createErrorResponse(message: string, possibleSolutions?: string[]): {
content: Array<{
type: 'text';
text: string;
}>;
isError: boolean;
};
/**
* Wrap a structured payload as a ToolResponse that satisfies the MCP
* outputSchema contract. The same payload is emitted both as a JSON text
* content block (for lenient clients) and as `structuredContent` (required
* by strict clients per MCP spec revision 2025-06-18).
*
* `extraContent` is prepended for handlers that also return non-text blocks
* (e.g. `take_screenshot`'s inline image).
*/
export declare function createStructuredResponse<T extends Record<string, unknown>>(payload: T, extraContent?: Array<{
type: string;
[k: string]: unknown;
}>): ToolResponse;
interface ValidatedProjectArgs {
projectPath: string;
}
interface ValidatedSceneArgs {
projectPath: string;
scenePath: string;
}
type ValidationErrorResult = ReturnType<typeof createErrorResponse>;
export declare function validateProjectArgs(args: OperationParams): ValidatedProjectArgs | ValidationErrorResult;
export declare function validateSceneArgs(args: OperationParams, opts?: {
sceneRequired?: boolean;
}): ValidatedSceneArgs | ValidationErrorResult;
export declare class GodotRunner {
private godotPath;
private operationsScriptPath;
private bridge;
private validatedPaths;
private cachedVersion;
activeProcess: GodotProcess | null;
activeProjectPath: string | null;
activeSessionMode: RuntimeSessionMode | null;
activeBridgePort: number | null;
private socket;
private rxChunks;
private rxTotal;
private inFlight;
constructor(config?: GodotServerConfig);
private isValidGodotPathSync;
private spawnAsync;
private isValidGodotPath;
detectGodotPath(): Promise<void>;
getGodotPath(): string | null;
/**
* Read the port currently baked into the project's bridge script. Returns
* null if the file is missing or malformed. Thin pass-through to
* BridgeManager — used by bridge-wait-timeout race detection.
*/
readBakedBridgePort(projectPath: string): number | null;
getVersion(): Promise<string>;
executeOperation(operation: string, params: OperationParams, projectPath: string, timeoutMs?: number): Promise<OperationResult>;
launchEditor(projectPath: string): ChildProcess;
runProject(projectPath: string, scene?: string, background?: boolean, bridgePort?: number): Promise<GodotProcess>;
attachProject(projectPath: string, bridgePort?: number): Promise<void>;
stopProject(): Promise<RuntimeStopResult | null>;
hasActiveRuntimeSession(): boolean;
/**
* Send a JSON command to the McpBridge over a long-lived TCP connection.
*
* MCP serializes tool calls so we hold one in-flight command at a time. The
* socket is lazy-connected on first call and persists across commands until
* `closeConnection` (or a peer-side close). A close mid-flight rejects with
* `BridgeDisconnectedError`; a per-command timeout rejects but does NOT
* close the socket — a slow command does not invalidate the session.
*/
sendCommand(command: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<string>;
/**
* Tear down the bridge socket. Idempotent. Any in-flight command is
* rejected with a session-ended error.
*/
closeConnection(): void;
private resetRxBuffer;
getErrorCount(): number;
getErrorsSince(marker: number): string[];
private static readonly SCRIPT_ERROR_PATTERNS;
private static readonly RETRYABLE_BRIDGE_COMMANDS;
extractRuntimeErrors(lines: string[]): string[];
private sendCommandWithReconnect;
sendCommandWithErrors(command: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<{
response: string;
runtimeErrors: string[];
}>;
/**
* Shared poll loop for `waitForBridge` (spawned) and `waitForBridgeAttached`.
* Sends `ping` payloads until the bridge replies with a pong that
* `validatePong` accepts, the deadline passes, or `shouldAbort` reports
* the spawned process has exited.
*/
private pollBridge;
waitForBridgeAttached(timeoutMs?: number, intervalMs?: number): Promise<{
ready: boolean;
error?: string;
}>;
waitForBridge(timeoutMs?: number, intervalMs?: number): Promise<{
ready: boolean;
error?: string;
}>;
getRecentErrors(count?: number): string[];
}
export {};
//# sourceMappingURL=godot-runner.d.ts.map