@tmhs/godot-correctness-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +34 -0
- package/README.md +118 -0
- package/data/godot-4.4.symbols.json.gz +0 -0
- package/data/godot-4.5.symbols.json.gz +0 -0
- package/data/tree-sitter-gdscript.wasm +0 -0
- package/dist/capabilities.d.ts +21 -0
- package/dist/capabilities.js +24 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +105 -0
- package/dist/cli.js.map +1 -0
- package/dist/gdscript/ast.d.ts +25 -0
- package/dist/gdscript/ast.js +82 -0
- package/dist/gdscript/ast.js.map +1 -0
- package/dist/gdscript/parser.d.ts +7 -0
- package/dist/gdscript/parser.js +44 -0
- package/dist/gdscript/parser.js.map +1 -0
- package/dist/godot/text-format.d.ts +35 -0
- package/dist/godot/text-format.js +105 -0
- package/dist/godot/text-format.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/lint/config.d.ts +83 -0
- package/dist/lint/config.js +55 -0
- package/dist/lint/config.js.map +1 -0
- package/dist/lint/engine.d.ts +7 -0
- package/dist/lint/engine.js +33 -0
- package/dist/lint/engine.js.map +1 -0
- package/dist/lint/project.d.ts +12 -0
- package/dist/lint/project.js +49 -0
- package/dist/lint/project.js.map +1 -0
- package/dist/lint/rules/await-misuse.d.ts +3 -0
- package/dist/lint/rules/await-misuse.js +34 -0
- package/dist/lint/rules/await-misuse.js.map +1 -0
- package/dist/lint/rules/connect-without-disconnect.d.ts +7 -0
- package/dist/lint/rules/connect-without-disconnect.js +38 -0
- package/dist/lint/rules/connect-without-disconnect.js.map +1 -0
- package/dist/lint/rules/delta-misuse.d.ts +7 -0
- package/dist/lint/rules/delta-misuse.js +78 -0
- package/dist/lint/rules/delta-misuse.js.map +1 -0
- package/dist/lint/rules/float-grid-equality.d.ts +6 -0
- package/dist/lint/rules/float-grid-equality.js +39 -0
- package/dist/lint/rules/float-grid-equality.js.map +1 -0
- package/dist/lint/rules/get-node-in-process.d.ts +3 -0
- package/dist/lint/rules/get-node-in-process.js +31 -0
- package/dist/lint/rules/get-node-in-process.js.map +1 -0
- package/dist/lint/rules/index.d.ts +4 -0
- package/dist/lint/rules/index.js +21 -0
- package/dist/lint/rules/index.js.map +1 -0
- package/dist/lint/rules/missing-onready.d.ts +3 -0
- package/dist/lint/rules/missing-onready.js +52 -0
- package/dist/lint/rules/missing-onready.js.map +1 -0
- package/dist/lint/rules/stringly-nodepath.d.ts +3 -0
- package/dist/lint/rules/stringly-nodepath.js +35 -0
- package/dist/lint/rules/stringly-nodepath.js.map +1 -0
- package/dist/lint/rules/untyped-declaration.d.ts +7 -0
- package/dist/lint/rules/untyped-declaration.js +25 -0
- package/dist/lint/rules/untyped-declaration.js.map +1 -0
- package/dist/lint/types.d.ts +36 -0
- package/dist/lint/types.js +2 -0
- package/dist/lint/types.js.map +1 -0
- package/dist/resources/skills.d.ts +12 -0
- package/dist/resources/skills.js +45 -0
- package/dist/resources/skills.js.map +1 -0
- package/dist/scenes/paths.d.ts +14 -0
- package/dist/scenes/paths.js +38 -0
- package/dist/scenes/paths.js.map +1 -0
- package/dist/scenes/report.d.ts +17 -0
- package/dist/scenes/report.js +25 -0
- package/dist/scenes/report.js.map +1 -0
- package/dist/scenes/validate.d.ts +16 -0
- package/dist/scenes/validate.js +185 -0
- package/dist/scenes/validate.js.map +1 -0
- package/dist/server-info.d.ts +13 -0
- package/dist/server-info.js +21 -0
- package/dist/server-info.js.map +1 -0
- package/dist/symbols/db.d.ts +11 -0
- package/dist/symbols/db.js +42 -0
- package/dist/symbols/db.js.map +1 -0
- package/dist/symbols/lookup.d.ts +89 -0
- package/dist/symbols/lookup.js +271 -0
- package/dist/symbols/lookup.js.map +1 -0
- package/dist/symbols/schema.d.ts +1419 -0
- package/dist/symbols/schema.js +109 -0
- package/dist/symbols/schema.js.map +1 -0
- package/dist/tools/api.d.ts +3 -0
- package/dist/tools/api.js +91 -0
- package/dist/tools/api.js.map +1 -0
- package/dist/tools/lint.d.ts +3 -0
- package/dist/tools/lint.js +45 -0
- package/dist/tools/lint.js.map +1 -0
- package/dist/tools/scenes.d.ts +3 -0
- package/dist/tools/scenes.js +43 -0
- package/dist/tools/scenes.js.map +1 -0
- package/package.json +57 -0
- package/skills/data-driven-resources.md +78 -0
- package/skills/resource-preload.md +43 -0
- package/skills/signal-hygiene.md +46 -0
- package/skills/typed-gdscript.md +47 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Schema for the compact Godot symbol database produced by
|
|
4
|
+
* scripts/generate-symbol-db.ts from `godot --dump-extension-api`. Only the
|
|
5
|
+
* fields needed for API lookup and cross-version diffing are kept; hashes,
|
|
6
|
+
* memory offsets, class sizes, and native structures are stripped.
|
|
7
|
+
*
|
|
8
|
+
* Bump SCHEMA_VERSION when the shape changes so stale artifacts fail validation.
|
|
9
|
+
*/
|
|
10
|
+
export const SCHEMA_VERSION = 1;
|
|
11
|
+
const argSchema = z.object({
|
|
12
|
+
name: z.string(),
|
|
13
|
+
type: z.string(),
|
|
14
|
+
/** Default value as Godot renders it, e.g. "false", "0", "Vector2(0, 0)". */
|
|
15
|
+
default: z.string().optional(),
|
|
16
|
+
});
|
|
17
|
+
const methodSchema = z.object({
|
|
18
|
+
name: z.string(),
|
|
19
|
+
/** Return type name, or null for void. */
|
|
20
|
+
ret: z.string().nullable(),
|
|
21
|
+
args: z.array(argSchema),
|
|
22
|
+
isConst: z.boolean().optional(),
|
|
23
|
+
isStatic: z.boolean().optional(),
|
|
24
|
+
isVirtual: z.boolean().optional(),
|
|
25
|
+
isVararg: z.boolean().optional(),
|
|
26
|
+
});
|
|
27
|
+
const propertySchema = z.object({
|
|
28
|
+
name: z.string(),
|
|
29
|
+
type: z.string(),
|
|
30
|
+
getter: z.string().optional(),
|
|
31
|
+
setter: z.string().optional(),
|
|
32
|
+
});
|
|
33
|
+
const signalSchema = z.object({
|
|
34
|
+
name: z.string(),
|
|
35
|
+
args: z.array(argSchema),
|
|
36
|
+
});
|
|
37
|
+
const enumSchema = z.object({
|
|
38
|
+
name: z.string(),
|
|
39
|
+
isBitfield: z.boolean().optional(),
|
|
40
|
+
values: z.array(z.object({ name: z.string(), value: z.number() })),
|
|
41
|
+
});
|
|
42
|
+
const classConstantSchema = z.object({ name: z.string(), value: z.number() });
|
|
43
|
+
const classSchema = z.object({
|
|
44
|
+
inherits: z.string().nullable(),
|
|
45
|
+
apiType: z.string(),
|
|
46
|
+
isRefcounted: z.boolean().optional(),
|
|
47
|
+
isInstantiable: z.boolean().optional(),
|
|
48
|
+
methods: z.array(methodSchema),
|
|
49
|
+
properties: z.array(propertySchema),
|
|
50
|
+
signals: z.array(signalSchema),
|
|
51
|
+
enums: z.array(enumSchema),
|
|
52
|
+
constants: z.array(classConstantSchema),
|
|
53
|
+
});
|
|
54
|
+
const builtinOperatorSchema = z.object({
|
|
55
|
+
name: z.string(),
|
|
56
|
+
right: z.string().nullable(),
|
|
57
|
+
ret: z.string(),
|
|
58
|
+
});
|
|
59
|
+
const builtinConstantSchema = z.object({
|
|
60
|
+
name: z.string(),
|
|
61
|
+
type: z.string(),
|
|
62
|
+
value: z.string(),
|
|
63
|
+
});
|
|
64
|
+
const builtinClassSchema = z.object({
|
|
65
|
+
members: z.array(z.object({ name: z.string(), type: z.string() })),
|
|
66
|
+
constants: z.array(builtinConstantSchema),
|
|
67
|
+
enums: z.array(enumSchema),
|
|
68
|
+
operators: z.array(builtinOperatorSchema),
|
|
69
|
+
methods: z.array(methodSchema),
|
|
70
|
+
isKeyed: z.boolean().optional(),
|
|
71
|
+
});
|
|
72
|
+
const utilityFunctionSchema = z.object({
|
|
73
|
+
name: z.string(),
|
|
74
|
+
ret: z.string().nullable(),
|
|
75
|
+
category: z.string(),
|
|
76
|
+
args: z.array(argSchema),
|
|
77
|
+
isVararg: z.boolean().optional(),
|
|
78
|
+
});
|
|
79
|
+
const metaSchema = z.object({
|
|
80
|
+
/** Full patch version, e.g. "4.4.1". */
|
|
81
|
+
godotVersion: z.string(),
|
|
82
|
+
/** Minor-line label used to name the artifact, e.g. "4.4". */
|
|
83
|
+
versionLabel: z.string(),
|
|
84
|
+
/** Godot's own version string, e.g. "4.4.1.stable.official". */
|
|
85
|
+
versionFull: z.string(),
|
|
86
|
+
/** Release tag the binary came from, e.g. "4.4.1-stable". */
|
|
87
|
+
releaseTag: z.string().nullable(),
|
|
88
|
+
/** Downloaded archive filename, if the binary was fetched. */
|
|
89
|
+
archiveName: z.string().nullable(),
|
|
90
|
+
/** SHA-256 of the downloaded archive, if fetched. */
|
|
91
|
+
archiveSha256: z.string().nullable(),
|
|
92
|
+
/** Provenance of the binary, e.g. "godotengine/godot-builds" or "local". */
|
|
93
|
+
source: z.string(),
|
|
94
|
+
schemaVersion: z.literal(SCHEMA_VERSION),
|
|
95
|
+
generatedAt: z.string(),
|
|
96
|
+
});
|
|
97
|
+
export const symbolDbSchema = z.object({
|
|
98
|
+
meta: metaSchema,
|
|
99
|
+
classes: z.record(z.string(), classSchema),
|
|
100
|
+
builtinClasses: z.record(z.string(), builtinClassSchema),
|
|
101
|
+
utilityFunctions: z.array(utilityFunctionSchema),
|
|
102
|
+
globalEnums: z.array(enumSchema),
|
|
103
|
+
singletons: z.array(z.object({ name: z.string(), type: z.string() })),
|
|
104
|
+
});
|
|
105
|
+
/** Parse and validate an already-decompressed DB object. Throws on mismatch. */
|
|
106
|
+
export function parseSymbolDb(data) {
|
|
107
|
+
return symbolDbSchema.parse(data);
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/symbols/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { availableVersions, loadSymbolDb } from "../symbols/db.js";
|
|
3
|
+
import { classSummary, diffClass, diffSymbol, lookupSymbol, } from "../symbols/lookup.js";
|
|
4
|
+
const VERSION = z.enum(["4.4", "4.5"]);
|
|
5
|
+
const KIND = z.enum([
|
|
6
|
+
"class",
|
|
7
|
+
"method",
|
|
8
|
+
"property",
|
|
9
|
+
"signal",
|
|
10
|
+
"enum",
|
|
11
|
+
"constant",
|
|
12
|
+
"builtin_class",
|
|
13
|
+
"builtin_method",
|
|
14
|
+
"utility_function",
|
|
15
|
+
"singleton",
|
|
16
|
+
"global_enum",
|
|
17
|
+
]);
|
|
18
|
+
function ok(data) {
|
|
19
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
20
|
+
}
|
|
21
|
+
function fail(message) {
|
|
22
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: message }, null, 2) }] };
|
|
23
|
+
}
|
|
24
|
+
/** Register the API symbol lookup / summary / diff tools. */
|
|
25
|
+
export function registerApiTools(server) {
|
|
26
|
+
server.tool("api_symbol_lookup", "Look up a Godot API symbol (class, method, property, or signal) by name across " +
|
|
27
|
+
"the pinned Godot versions. Supports 'Owner.member' queries. Returns full " +
|
|
28
|
+
"signature data, or closest-match suggestions on a miss.", {
|
|
29
|
+
symbol: z.string().describe("Symbol name, e.g. 'Node', 'move_and_slide', or 'Node.get_child_count'"),
|
|
30
|
+
version: VERSION.optional().describe("Limit to one version; omit to search all available versions"),
|
|
31
|
+
kind: KIND.optional().describe("Restrict results to one symbol kind"),
|
|
32
|
+
}, async (args) => {
|
|
33
|
+
const { symbol, version, kind } = args;
|
|
34
|
+
const versions = version ? [version] : availableVersions();
|
|
35
|
+
if (versions.length === 0)
|
|
36
|
+
return fail("No symbol databases are available.");
|
|
37
|
+
try {
|
|
38
|
+
const byVersion = Object.fromEntries(versions.map((v) => [v, lookupSymbol(loadSymbolDb(v), symbol, kind ? { kind } : {})]));
|
|
39
|
+
return ok({ symbol, versions, results: byVersion });
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
server.tool("api_class_summary", "Full member listing for one Godot class in one version: inheritance chain, " +
|
|
46
|
+
"methods, properties, signals, enums, and constants.", {
|
|
47
|
+
class: z.string().describe("Class name, e.g. 'CharacterBody2D'"),
|
|
48
|
+
version: VERSION.describe("Which Godot version to summarize"),
|
|
49
|
+
}, async (args) => {
|
|
50
|
+
const { class: className, version } = args;
|
|
51
|
+
try {
|
|
52
|
+
const db = loadSymbolDb(version);
|
|
53
|
+
const summary = classSummary(db, className);
|
|
54
|
+
if (!summary) {
|
|
55
|
+
const suggestions = lookupSymbol(db, className, { kind: "class" }).suggestions ?? [];
|
|
56
|
+
return fail(`Class '${className}' not found in Godot ${version}.` +
|
|
57
|
+
(suggestions.length ? ` Did you mean: ${suggestions.map((s) => s.name).join(", ")}?` : ""));
|
|
58
|
+
}
|
|
59
|
+
return ok({ version, ...summary });
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
server.tool("api_diff", "Diff a Godot class or symbol between two versions (default 4.4 -> 4.5): what " +
|
|
66
|
+
"was added, removed, or had its signature changed, with old and new signatures " +
|
|
67
|
+
"side by side.", {
|
|
68
|
+
symbol: z.string().describe("Class name (e.g. 'TileMap') or symbol (e.g. 'move_and_slide')"),
|
|
69
|
+
from: VERSION.optional().describe("Base version (default 4.4)"),
|
|
70
|
+
to: VERSION.optional().describe("Target version (default 4.5)"),
|
|
71
|
+
}, async (args) => {
|
|
72
|
+
const { symbol, from = "4.4", to = "4.5" } = args;
|
|
73
|
+
if (from === to)
|
|
74
|
+
return fail("'from' and 'to' must be different versions.");
|
|
75
|
+
try {
|
|
76
|
+
const fromDb = loadSymbolDb(from);
|
|
77
|
+
const toDb = loadSymbolDb(to);
|
|
78
|
+
// A dotted query is always a member; otherwise try a class diff first.
|
|
79
|
+
if (!symbol.includes(".")) {
|
|
80
|
+
const cd = diffClass(fromDb, toDb, symbol);
|
|
81
|
+
if (cd.presence.inFrom || cd.presence.inTo)
|
|
82
|
+
return ok({ type: "class", diff: cd });
|
|
83
|
+
}
|
|
84
|
+
return ok({ type: "symbol", diff: diffSymbol(fromDb, toDb, symbol) });
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/tools/api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EACL,YAAY,EACZ,SAAS,EACT,UAAU,EACV,YAAY,GAEb,MAAM,sBAAsB,CAAC;AAE9B,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AACvC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;IAClB,OAAO;IACP,QAAQ;IACR,UAAU;IACV,QAAQ;IACR,MAAM;IACN,UAAU;IACV,eAAe;IACf,gBAAgB;IAChB,kBAAkB;IAClB,WAAW;IACX,aAAa;CACd,CAAC,CAAC;AAIH,SAAS,EAAE,CAAC,IAAa;IACvB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED,SAAS,IAAI,CAAC,OAAe;IAC3B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC5F,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAChD,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,iFAAiF;QAC/E,2EAA2E;QAC3E,yDAAyD,EAC3D;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uEAAuE,CAAC;QACpG,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6DAA6D,CAAC;QACnG,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;KACtE,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAIjC,CAAC;QACF,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC;QAC3D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,oCAAoC,CAAC,CAAC;QAC7E,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAClC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CACtF,CAAC;YACF,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,6EAA6E;QAC3E,qDAAqD,EACvD;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;QAChE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,kCAAkC,CAAC;KAC9D,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAiD,CAAC;QACxF,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;YACjC,MAAM,OAAO,GAAG,YAAY,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,WAAW,GAAG,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC;gBACrF,OAAO,IAAI,CACT,UAAU,SAAS,wBAAwB,OAAO,GAAG;oBACnD,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,kBAAkB,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAC7F,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,UAAU,EACV,+EAA+E;QAC7E,gFAAgF;QAChF,eAAe,EACjB;QACE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+DAA+D,CAAC;QAC5F,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;QAC/D,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;KAChE,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,IAI5C,CAAC;QACF,IAAI,IAAI,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC,6CAA6C,CAAC,CAAC;QAC5E,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,IAAI,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;YAC9B,uEAAuE;YACvE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1B,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC3C,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI;oBAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YACrF,CAAC;YACD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACxE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { loadConfig } from "../lint/config.js";
|
|
5
|
+
import { lintSource } from "../lint/engine.js";
|
|
6
|
+
import { lintProject } from "../lint/project.js";
|
|
7
|
+
function ok(data) {
|
|
8
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
9
|
+
}
|
|
10
|
+
function fail(message) {
|
|
11
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: message }, null, 2) }] };
|
|
12
|
+
}
|
|
13
|
+
/** Register the GDScript lint tools (lint_file, lint_project). */
|
|
14
|
+
export function registerLintTools(server) {
|
|
15
|
+
server.tool("lint_file", "Lint a single GDScript (.gd) file against the default ruleset. Provide either " +
|
|
16
|
+
"inline source or a path on disk. Config is loaded from the file's directory " +
|
|
17
|
+
"if a godot-correctness.config.json is present. Returns a findings array.", {
|
|
18
|
+
path: z.string().describe("Path to the .gd file (used for the reported location and config lookup)"),
|
|
19
|
+
source: z.string().optional().describe("Inline GDScript source; if omitted, the file at 'path' is read"),
|
|
20
|
+
}, async (args) => {
|
|
21
|
+
const { path, source } = args;
|
|
22
|
+
try {
|
|
23
|
+
const code = source ?? readFileSync(path, "utf-8");
|
|
24
|
+
const config = loadConfig(dirname(path));
|
|
25
|
+
const findings = await lintSource(path, code, config);
|
|
26
|
+
return ok({ file: path, findings, count: findings.length });
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
server.tool("lint_project", "Lint every GDScript file under a project root, honoring include/exclude globs " +
|
|
33
|
+
"from godot-correctness.config.json. Returns aggregated findings and severity counts.", {
|
|
34
|
+
path: z.string().describe("Path to the Godot project root"),
|
|
35
|
+
}, async (args) => {
|
|
36
|
+
const { path } = args;
|
|
37
|
+
try {
|
|
38
|
+
return ok(await lintProject(path));
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=lint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/tools/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIjD,SAAS,EAAE,CAAC,IAAa;IACvB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED,SAAS,IAAI,CAAC,OAAe;IAC3B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC5F,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,CAAC,IAAI,CACT,WAAW,EACX,gFAAgF;QAC9E,8EAA8E;QAC9E,0EAA0E,EAC5E;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yEAAyE,CAAC;QACpG,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gEAAgE,CAAC;KACzG,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAyC,CAAC;QACnE,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YACtD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,cAAc,EACd,gFAAgF;QAC9E,sFAAsF,EACxF;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC;KAC5D,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,EAAE,IAAI,EAAE,GAAG,IAAwB,CAAC;QAC1C,IAAI,CAAC;YACH,OAAO,EAAE,CAAC,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { projectReport } from "../scenes/report.js";
|
|
3
|
+
import { validateProjectConfig, validateRegistries, validateScenes, } from "../scenes/validate.js";
|
|
4
|
+
function ok(data) {
|
|
5
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
6
|
+
}
|
|
7
|
+
function fail(message) {
|
|
8
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: message }, null, 2) }] };
|
|
9
|
+
}
|
|
10
|
+
function guard(fn) {
|
|
11
|
+
try {
|
|
12
|
+
return ok(fn());
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Register the scene / resource / registry static-analysis tools. */
|
|
19
|
+
export function registerSceneTools(server) {
|
|
20
|
+
server.tool("validate_scenes", "Validate every .tscn under a project root: ext_resource paths resolve, " +
|
|
21
|
+
"ExtResource references are declared, and there are no duplicate resource ids.", { path: z.string().describe("Path to the Godot project root") }, async (args) => guard(() => {
|
|
22
|
+
const findings = validateScenes(args.path);
|
|
23
|
+
return { findings, count: findings.length };
|
|
24
|
+
}));
|
|
25
|
+
server.tool("validate_project_config", "Validate project.godot: the main scene exists, autoload paths exist, and " +
|
|
26
|
+
"input actions are well-formed.", { path: z.string().describe("Path to the Godot project root") }, async (args) => guard(() => {
|
|
27
|
+
const findings = validateProjectConfig(args.path);
|
|
28
|
+
return { findings, count: findings.length };
|
|
29
|
+
}));
|
|
30
|
+
server.tool("validate_registries", "Deep-validate data-driven .tres registries in the configured registryDirs: " +
|
|
31
|
+
"each parses, its script resolves (recording class_name), and every " +
|
|
32
|
+
"resource-path property points at an existing file (the dangling-path check).", { path: z.string().describe("Path to the Godot project root") }, async (args) => guard(() => validateRegistries(args.path)));
|
|
33
|
+
server.tool("project_report", "Aggregate all static checks (scenes, project config, registries, and " +
|
|
34
|
+
"GDScript lint) into one report with severity counts.", { path: z.string().describe("Path to the Godot project root") }, async (args) => {
|
|
35
|
+
try {
|
|
36
|
+
return ok(await projectReport(args.path));
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
return fail(err instanceof Error ? err.message : String(err));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=scenes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scenes.js","sourceRoot":"","sources":["../../src/tools/scenes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAI/B,SAAS,EAAE,CAAC,IAAa;IACvB,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9E,CAAC;AACD,SAAS,IAAI,CAAC,OAAe;IAC3B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC5F,CAAC;AACD,SAAS,KAAK,CAAC,EAAiB;IAC9B,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,kBAAkB,CAAC,MAAiB;IAClD,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,yEAAyE;QACvE,+EAA+E,EACjF,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC,EAAE,EAC/D,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;QACzB,MAAM,QAAQ,GAAG,cAAc,CAAE,IAAyB,CAAC,IAAI,CAAC,CAAC;QACjE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC9C,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,yBAAyB,EACzB,2EAA2E;QACzE,gCAAgC,EAClC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC,EAAE,EAC/D,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;QACzB,MAAM,QAAQ,GAAG,qBAAqB,CAAE,IAAyB,CAAC,IAAI,CAAC,CAAC;QACxE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC9C,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,qBAAqB,EACrB,6EAA6E;QAC3E,qEAAqE;QACrE,8EAA8E,EAChF,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC,EAAE,EAC/D,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAE,IAAyB,CAAC,IAAI,CAAC,CAAC,CACjF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,uEAAuE;QACrE,sDAAsD,EACxD,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gCAAgC,CAAC,EAAE,EAC/D,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,OAAO,EAAE,CAAC,MAAM,aAAa,CAAE,IAAyB,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tmhs/godot-correctness-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Offline static correctness MCP server for Godot 4.x GDScript projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"godot-correctness-mcp": "dist/index.js",
|
|
9
|
+
"gdcorrect": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"data",
|
|
14
|
+
"skills",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
22
|
+
"validate:artifacts": "tsx scripts/validate-symbol-db.ts",
|
|
23
|
+
"dev": "tsx watch src/index.ts",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
+
"picomatch": "^4.0.5",
|
|
29
|
+
"web-tree-sitter": "^0.26.11",
|
|
30
|
+
"zod": "^3.25.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^22.10.0",
|
|
34
|
+
"@types/picomatch": "^4.0.3",
|
|
35
|
+
"tsx": "^4.19.4",
|
|
36
|
+
"typescript": "^5.6.0",
|
|
37
|
+
"vitest": "^4.1.8"
|
|
38
|
+
},
|
|
39
|
+
"author": "TMHSDigital",
|
|
40
|
+
"license": "CC-BY-NC-ND-4.0",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/TMHSDigital/godot-correctness-mcp.git"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/TMHSDigital/godot-correctness-mcp#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/TMHSDigital/godot-correctness-mcp/issues"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"mcp",
|
|
51
|
+
"model-context-protocol",
|
|
52
|
+
"developer-tools"
|
|
53
|
+
],
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=20.0.0"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Data-driven resources and registry loading
|
|
2
|
+
|
|
3
|
+
Custom `Resource` types let you drive game content from `.tres` files instead of
|
|
4
|
+
hardcoding it. Keep resources as **pure data**, load them through a validated
|
|
5
|
+
**registry**, and this server's `validate_registries` tool can catch dangling
|
|
6
|
+
references before they fail at runtime.
|
|
7
|
+
|
|
8
|
+
## Keep Resource state data-only
|
|
9
|
+
|
|
10
|
+
A data resource should hold values, not runtime/session state. Runtime state
|
|
11
|
+
(current health, cooldown timers, whoever owns it) belongs on the node that uses
|
|
12
|
+
the resource, not on the shared resource instance — resources are shared by
|
|
13
|
+
reference, so mutating one at runtime mutates it everywhere and can leak into
|
|
14
|
+
saved copies.
|
|
15
|
+
|
|
16
|
+
```gdscript
|
|
17
|
+
# item.gd - data only
|
|
18
|
+
extends Resource
|
|
19
|
+
class_name ItemData
|
|
20
|
+
|
|
21
|
+
@export var id: StringName
|
|
22
|
+
@export var display_name: String
|
|
23
|
+
@export var icon: Texture2D
|
|
24
|
+
@export var scene_path: String # path to a .tscn to spawn; validated statically
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```gdscript
|
|
28
|
+
# WRONG: runtime state on the shared data resource
|
|
29
|
+
extends Resource
|
|
30
|
+
class_name ItemData
|
|
31
|
+
@export var display_name: String
|
|
32
|
+
var current_stack := 0 # <- mutated at runtime, shared across every holder
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Put `current_stack` on the inventory node instead, keyed by `ItemData`.
|
|
36
|
+
|
|
37
|
+
## Registry-loading pattern
|
|
38
|
+
|
|
39
|
+
Load a directory of `.tres` into a typed dictionary once, keyed by a stable id.
|
|
40
|
+
|
|
41
|
+
```gdscript
|
|
42
|
+
# item_registry.gd (autoload)
|
|
43
|
+
extends Node
|
|
44
|
+
|
|
45
|
+
const ITEM_DIR := "res://resources/items"
|
|
46
|
+
var _items: Dictionary[StringName, ItemData] = {}
|
|
47
|
+
|
|
48
|
+
func _ready() -> void:
|
|
49
|
+
for file in DirAccess.get_files_at(ITEM_DIR):
|
|
50
|
+
if not file.ends_with(".tres"):
|
|
51
|
+
continue
|
|
52
|
+
var res := load(ITEM_DIR.path_join(file)) as ItemData
|
|
53
|
+
if res == null:
|
|
54
|
+
push_error("Not an ItemData: %s" % file)
|
|
55
|
+
continue
|
|
56
|
+
_items[res.id] = res
|
|
57
|
+
|
|
58
|
+
func get_item(id: StringName) -> ItemData:
|
|
59
|
+
return _items.get(id)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Validate before runtime
|
|
63
|
+
|
|
64
|
+
String path properties like `scene_path` above are invisible failures: a renamed
|
|
65
|
+
or deleted `.tscn` only errors when something actually loads it. Configure the
|
|
66
|
+
registry directory so the static validator checks every `.tres`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
// godot-correctness.config.json
|
|
70
|
+
{
|
|
71
|
+
"registryDirs": ["resources/items"],
|
|
72
|
+
"pathPropertyPatterns": ["*_path", "*_scene"]
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Then `validate_registries` (or `gdcorrect validate-registries .`) confirms each
|
|
77
|
+
`.tres` parses, its script resolves, and every resource-path property points at
|
|
78
|
+
an existing file. See also [[signal-hygiene]] and [[typed-gdscript]].
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# preload vs load
|
|
2
|
+
|
|
3
|
+
Choose between `preload` and `load` by *when* you need the resource and whether
|
|
4
|
+
the path is known at compile time.
|
|
5
|
+
|
|
6
|
+
## preload: compile-time, known path
|
|
7
|
+
|
|
8
|
+
`preload` resolves at parse time and bakes the dependency into the script. Use it
|
|
9
|
+
for assets you always need and whose path is a constant literal.
|
|
10
|
+
|
|
11
|
+
```gdscript
|
|
12
|
+
const BulletScene := preload("res://actors/bullet.tscn")
|
|
13
|
+
|
|
14
|
+
func fire() -> void:
|
|
15
|
+
var b := BulletScene.instantiate()
|
|
16
|
+
add_child(b)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Because `preload` needs a constant literal path, prefer it over `load("res://...")`
|
|
20
|
+
with a hardcoded string — it fails at compile time (not runtime) if the path is
|
|
21
|
+
wrong, and it does not re-hit the disk cache each call.
|
|
22
|
+
|
|
23
|
+
## load: runtime, dynamic path
|
|
24
|
+
|
|
25
|
+
Use `load` when the path is computed (e.g. from a data resource) or the asset is
|
|
26
|
+
optional/lazy.
|
|
27
|
+
|
|
28
|
+
```gdscript
|
|
29
|
+
func spawn_from(item: ItemData) -> Node:
|
|
30
|
+
var scene := load(item.scene_path) as PackedScene # path came from data
|
|
31
|
+
return scene.instantiate() if scene else null
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Data-driven paths like `item.scene_path` are exactly what
|
|
35
|
+
[[data-driven-resources]] and the `validate_registries` tool guard: a dangling
|
|
36
|
+
`res://` path here is invisible until this line runs. Validate registries in CI
|
|
37
|
+
so a bad path fails the build, not the player's session.
|
|
38
|
+
|
|
39
|
+
## Do not preload inside hot loops
|
|
40
|
+
|
|
41
|
+
`preload` is resolved once at parse time regardless of where it appears, but keep
|
|
42
|
+
`load()` calls out of `_process`/`_physics_process`; cache the `PackedScene` once.
|
|
43
|
+
See [[typed-gdscript]].
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Signal hygiene
|
|
2
|
+
|
|
3
|
+
Godot 4 signals are first-class. Connect with the typed `Callable` API, avoid
|
|
4
|
+
frame-fragile lookups, and disconnect (or use one-shot) when a connection should
|
|
5
|
+
not outlive the node.
|
|
6
|
+
|
|
7
|
+
## Prefer the typed connect API
|
|
8
|
+
|
|
9
|
+
```gdscript
|
|
10
|
+
# Godot 4: reference the signal object and pass a Callable
|
|
11
|
+
button.pressed.connect(_on_button_pressed)
|
|
12
|
+
|
|
13
|
+
# Avoid the stringly-typed legacy form
|
|
14
|
+
button.connect("pressed", Callable(self, "_on_button_pressed"))
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Connect once, disconnect deliberately
|
|
18
|
+
|
|
19
|
+
Connecting in `_ready()` without cleanup risks double-connections (if the node
|
|
20
|
+
re-enters the tree) or dangling references (if the emitter outlives the receiver).
|
|
21
|
+
|
|
22
|
+
```gdscript
|
|
23
|
+
func _ready() -> void:
|
|
24
|
+
# One-shot: auto-disconnects after the first emission.
|
|
25
|
+
get_tree().create_timer(1.0).timeout.connect(_spawn, CONNECT_ONE_SHOT)
|
|
26
|
+
EventBus.player_died.connect(_on_player_died)
|
|
27
|
+
|
|
28
|
+
func _exit_tree() -> void:
|
|
29
|
+
if EventBus.player_died.is_connected(_on_player_died):
|
|
30
|
+
EventBus.player_died.disconnect(_on_player_died)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This server's `connect-without-disconnect` lint rule flags `connect()` in
|
|
34
|
+
`_ready()` when there is no matching `disconnect()` and no `CONNECT_ONE_SHOT`.
|
|
35
|
+
|
|
36
|
+
## await a signal instead of polling
|
|
37
|
+
|
|
38
|
+
```gdscript
|
|
39
|
+
func flash() -> void:
|
|
40
|
+
modulate = Color.RED
|
|
41
|
+
await get_tree().create_timer(0.1).timeout # await a real signal
|
|
42
|
+
modulate = Color.WHITE
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Never `await` a plain value — `await` expects a signal or a coroutine
|
|
46
|
+
(the `await-misuse` rule catches `await <literal>`). See [[typed-gdscript]].
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Typed GDScript patterns
|
|
2
|
+
|
|
3
|
+
Static types make GDScript faster (typed instructions) and let tools catch
|
|
4
|
+
mistakes. Prefer inference with `:=`, annotate function signatures, and cache
|
|
5
|
+
node references with `@onready`.
|
|
6
|
+
|
|
7
|
+
## Type every declaration
|
|
8
|
+
|
|
9
|
+
```gdscript
|
|
10
|
+
var speed := 300.0 # inferred float via :=
|
|
11
|
+
var target: Node2D = null # explicit type when there is no initializer
|
|
12
|
+
const MAX_HP := 100 # inferred int
|
|
13
|
+
|
|
14
|
+
func take_damage(amount: int) -> void:
|
|
15
|
+
_hp = maxi(_hp - amount, 0)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Avoid bare `var x = 3` — it is untyped. Use `:=` to infer or `: Type` to
|
|
19
|
+
annotate. The `untyped-declaration` rule flags declarations with neither.
|
|
20
|
+
|
|
21
|
+
## Cache node references with @onready
|
|
22
|
+
|
|
23
|
+
```gdscript
|
|
24
|
+
# Resolved once, after the node enters the tree.
|
|
25
|
+
@onready var sprite: Sprite2D = $Sprite2D
|
|
26
|
+
@onready var _agent: NavigationAgent2D = $NavigationAgent2D
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Do not look nodes up every frame:
|
|
30
|
+
|
|
31
|
+
```gdscript
|
|
32
|
+
# WRONG: $ / get_node() inside _process runs every frame
|
|
33
|
+
func _process(delta: float) -> void:
|
|
34
|
+
$Sprite2D.rotation += delta # get-node-in-process
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The `missing-onready` rule flags a class-level node-path member without
|
|
38
|
+
`@onready`; `get-node-in-process` flags per-frame lookups.
|
|
39
|
+
|
|
40
|
+
## Prefer $ over string paths
|
|
41
|
+
|
|
42
|
+
```gdscript
|
|
43
|
+
@onready var hp_bar := $UI/HPBar # rename-safe editor reference
|
|
44
|
+
# var hp_bar = get_node("UI/HPBar") # stringly-nodepath: fragile
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
See [[signal-hygiene]] and [[data-driven-resources]].
|