GSC for Godot
Write Call of Duty style GSC scripts for Godot 4.7, with a full language server, a debugger that
understands script threads, hot reload and a thread inspector.
GSC is the scripting language of the Call of Duty games. This extension works with the GSCGodot
runtime, which makes .gsc a Godot script language: attach a .gsc script to a node, wait 0.5;,
self waittill("body_entered", body);, level notify("round_start");, ent moveto(...), with the
node as self and Godot's properties, methods and signals one dot away. The baseline dialect is
Black Ops III (T7); the default dialect t7x adds implied function definitions (foo() {}), ?? and
??=, array, map and struct literals, assignable vector components and childthread.
Features
- Language server (bundled
gsc-lsp.exe): diagnostics as you type, completion (functions,
builtins, fields, event names, Godot classes, methods and properties), signature help, hover with
ScriptDoc and builtin docs, go to definition, find references, rename (also in .tscn signal
connections and exported property values), document and workspace symbols, semantic highlighting,
folding, and links on #using and #insert paths. It reads the Godot project's [gsc] settings, so
it checks scripts as the game runs them.
- Highlighting of
.gsc, .gsh and .csc files: definitions with autoexec, private or the
legacy function keyword, thread/childthread, waits and events (with the event names marked),
self/level/game, function pointers &fn and [[ fp ]](args), vector, array and struct literals,
#"hash", &"istring" and %anim references, directives including the Godot ones (#extends,
#export, #signal, #tool, #class_name), /# #/ dev blocks and /@ @/ ScriptDoc.
- Snippets for definitions, loops, event loops (
eventloop, forever), threads, classes, literals,
ScriptDoc, directives and the Godot callbacks. They use the implied definition form.
- Debugger for games launched from VS Code or attached to while they run, and for scripts on the
command-line runtime: breakpoints with conditions, hit counts and logpoints, function and exception
breakpoints, stepping that follows a script thread across
wait, Locals/Self/Level/Game/Thread/Notify
scopes, Set Value, and a Debug Console that runs GSC (self.hp = 100;, level notify("go");).
Stopping the game freezes game time, so stepping over wait 0.5 takes half a second of game time.
- Hot reload: saving a
.gsc or .gsh file reloads it into the running game. Threads parked in a
wait or waittill move to the new code where it still matches; errors go to the Problems view.
- GSC Threads view in Run and Debug: every script thread, grouped by owner entity, state or function,
with what it waits for and its endons; show a thread's stack, pin, kill, break when it resumes, break
only for one owner, or notify an owner.
- Addon installer: GSC: Install Addon into Godot Project puts the matching runtime into a Godot
project, registers the
GSC autoload, offers a starter scene and imports the project.
Requirements
- Windows x64 (the bundled language server, debugger and Godot addon are Windows builds).
- VS Code 1.91 or newer.
- Godot 4.7 for Windows, with its console executable (
Godot_v4.7.2-stable_win64_console.exe,
shipped next to the normal one), to run games. The language server works without Godot.
First-install tutorial
VS Code opens Get started with GSC for Godot after installation. Reopen it with
GSC: Get Started. GSC: Create Hello World Project creates a complete project in an empty
folder, with CLI and Godot launch configurations. Press F5 with GSC: Hello World (CLI) selected
to print Hello from GSC immediately, then follow the tutorial to install the Godot addon.
Use the hellogsc and println snippets for more examples. The full tutorial ships under
GSC: Open Documentation > Get started.
Setup in 5 steps
Install this extension.
Run GSC: Select Godot Executable and pick Godot's console executable (stored in
gsc.godot.path).
Open your Godot project folder (the one with project.godot) and run GSC: Install Addon into
Godot Project. It copies the addon to addons/gsc, adds the GSC autoload, offers to create
scenes/main.tscn and scripts/main.gsc when the project has no main scene, and imports the
project. Close the Godot editor first when you update an addon. A GSC autoload that names a
scene of your own is kept; the addon's own files (addons/gsc/...) are replaced on every install.
Read-only files (a version-control checkout) are reported, not overwritten.
Write scripts/main.gsc; its autoexec functions and main() run when the game starts:
main()
{
println("Hello from GSC");
}
In the Godot editor, attach GSC scripts to nodes with Attach Script (language GSC).
Press F5 and choose GSC: Launch game (or add it with Run > Add Configuration...). Set a
breakpoint, save a change while the game runs, and open the GSC Threads view.
Settings
| Setting |
Default |
Purpose |
gsc.godot.path |
"" |
Godot's console executable, for launching and importing. Set it with GSC: Select Godot Executable |
gsc.addon.path |
"" |
The addon GSC: Install Addon installs: an addons/gsc folder, a folder containing one, or a zip. Empty: the addon bundled with the extension |
gsc.dialect |
auto |
t7x, t7, or auto to use the project's gsc/dialect setting |
gsc.hotReload.mode |
onSave |
onSave; watcher (the game's own file watcher reloads, project setting gsc/hot_reload/watch); off. A launch or attach configuration's hotReload overrides it |
gsc.debug.stopWhenSteppedThreadEnds |
true |
When the stepped thread ends, stop at the next frame instead of continuing |
gsc.debug.maxThreadsInCallStack |
64 |
How many script threads the Call Stack lists (the stopped one, pinned and focused ones, those at breakpoints, then the newest). A launch or attach configuration's maxThreadsInCallStack overrides it |
gsc.threadInspector.refreshInterval |
500 |
Milliseconds between refreshes of the GSC Threads view while the game runs (at least 250; 0 refreshes only on stops and on Refresh) |
gsc.threadInspector.groupBy |
owner |
owner, state, function or none |
gsc.cliPath |
"" |
The gsc.exe that GSC: Run CLI script debugs. Empty: GSC_CLI_PATH, then the bundled one |
gsc.lsp.path |
"" |
Run another gsc-lsp.exe instead of the bundled one (also GSC_LSP_PATH) |
gsc.trace.server |
off |
Trace the language server traffic (messages, verbose) |
Changing gsc.lsp.path or gsc.dialect restarts the language server. The game's own settings
(entry scripts, ticking, signal timing, debug port, ...) are Godot project settings under [gsc].
Commands
| Command |
Does |
| GSC: Select Godot Executable |
Sets gsc.godot.path |
| GSC: Install Addon into Godot Project |
Installs or updates the runtime addon in a project, writes the autoload, imports |
| GSC: Attach to Running Game |
Attaches the debugger to a running game picked from a list |
| GSC: Hot Reload Now |
Reloads the active file and every unsaved GSC file into the running game, unsaved text included |
| GSC: Regenerate Godot API from the Selected Godot |
Builds the Godot API data (classes, methods, properties, signals, docs) from the Godot in gsc.godot.path and restarts the language server with it (below) |
| GSC: Remove the Regenerated Godot API |
Deletes the regenerated API data; the language server goes back to the bundled Godot 4.7.2 data |
| GSC: Restart Language Server, GSC: Show Language Server Output |
Language server housekeeping |
| GSC: Open Documentation |
Opens one of the user guides that ship with the extension (below) in a Markdown preview |
| GSC Threads view commands |
Refresh Threads, Group Threads By..., Filter Threads..., Show Thread's Stack (Focus), Pause and Show Thread's Stack, Reveal Thread Source, Pin Thread to Call Stack, Unpin Thread, Kill Thread, Break When Thread Resumes, Don't Break When Thread Resumes, Break Only for This Owner, Break for All Owners, Notify Owner..., Show More Threads |
Godot API data
Completion, hover, signature help and the highlighting of Godot methods and properties come from
three layers, the later ones winning for a name they share:
- The bundled data (
data/api/godot-4.7.gscapi.json in the extension): Godot 4.7.2's classes,
builtin types, singletons, utility functions and global enums, with Godot's own documentation.
- A regenerated manifest: GSC: Regenerate Godot API from the Selected Godot runs the Godot of
gsc.godot.path with --headless --dump-extension-api-with-docs and converts the dump with the
bundled gsc-apigen.exe into the extension's global storage, then restarts the language server.
Use it for another Godot 4.x version or a custom build with extra engine modules. A project whose
config/features names another minor version uses the nearest manifest (logged in GSC: Show
Language Server Output). GSC: Remove the Regenerated Godot API deletes it again.
- The project overlay
.godot/gsc/project.gscapi.json, which the GSC addon's editor plugin
writes while the project is open in the Godot editor: the project's GDExtension classes, GDScript
class_name classes, autoloads and input actions. The language server watches it, so a class
added in Godot completes in VS Code a moment later.
Without Godot, the bundled data still completes the engine API; only the project's own classes need
the editor to have run once.
Debugging
Four configurations come as snippets in Run > Add Configuration...:
- GSC: Launch game (
"runtime": "godot") starts the project with the debugger connected. It checks
first that the addon is installed and the project imported. scene is "main" (the project's main
scene) or a scene file; args passes Godot arguments (after "--", to the game). With
waitForDebugger (default) scripts wait until your breakpoints are set. Stop ends the game.
- GSC: Attach (
"request": "attach") connects to a running debug build: a game started with
Godot's Play button or an exported debug build. target is pick, game (this project's newest
game), { "pid": n } or { "host", "port", "token" }. Debug builds listen on port 47110 (project
setting gsc/debug/listen_port). Disconnecting leaves the game running. An exported debug build
names no project folder: the configuration's project (default: the workspace folder) is taken as
res://, and the Debug Console says so. When the project is elsewhere, set
"pathMappings": [{ "localRoot": "<project folder>", "remoteRoot": "res://" }]; without any
mapping, breakpoints stay unverified and a save is refused with that hint.
- GSC: Attach to this project's game is the same with
"target": "game" and
"project": "${workspaceFolder}": it attaches to the newest running game of the workspace's
project without asking.
- GSC: Run CLI script (
"runtime": "cli") runs the current .gsc file on the command-line runtime
(the bundled gsc.exe), with a virtual clock and simulated entities. In a Godot project with the
addon installed it mounts addons/gsc/lib, so the helper library's #using paths resolve.
While the game is stopped, every script thread is stopped and game time stands still; the Godot
editor's own debugger is frozen too. The Call Stack lists up to 64 script threads (the stopped one,
pinned and focused ones, those at breakpoints, then the newest), named like
#1043 zombie_think#3 · zombie "z1" · waittill "damage". Exception breakpoints: Runtime errors,
Assertions and Infinite loops (on by default) and Script warnings, each with an optional
list of codes or message fragments as its condition.
Hot reload
During a debug session, saving a .gsc or .gsh file sends it to the game, and saves that come
together (Save All) reload as one batch. New calls run the new code at once; threads waiting in a
wait, waittill or call move to the new version where their position and live variables match,
and the others finish their old version (the GSC Debug output channel says which and why). Saving while stopped
at a breakpoint reloads in the break. Compile errors and reload warnings appear in the Problems view
(source gsc-runtime) until a later reload of the file succeeds; each reload is logged in the
GSC Debug output channel and flashes in the status bar. An exported debug build, or a game run from
another copy of the project, is sent the file's text.
Thread inspector
The GSC Threads view (Run and Debug, during a GSC session) lists every script thread, 200 at a
time, grouped by owner (the entity that was self when it started), by state or by entry function.
Each row shows what the thread waits for (wait 1.00, waittill "damage",
waittill "round_start" on level) and its endons; expanded, its owner, creator, childthreads, spawn
site and stack. It refreshes every 500 ms while the game runs, and the status bar shows
GSC: N threads. Right-click a thread or an owner group for its actions; Break Only for This
Owner makes breakpoints stop only in that entity's threads.
Known limits
- Windows x64 only.
- Godot 4.7 only; scripts export as text (no precompiled bytecode yet).
.csc client scripts are highlighted and analysed, not run.
- The debugger attaches to game processes only: tool scripts running inside the Godot editor cannot be
debugged, and breakpoints are set in VS Code (not in Godot's script editor gutter).
- One debug client per game at a time.
- GSC code runs on the main thread: GSC
_process of nodes in a sub-thread process group is not
called, and GSC methods called from worker threads run later on the main thread.
- No Set Expression, breakpoint location hints, data breakpoints or Restart Frame.
- The
aspire-gsc extension registers the same gsc language. When it is installed, this extension
shows a warning with a button that opens it in the Extensions view, so you can disable it for the
workspace.
Documentation
The user guides ship with the extension (its docs folder). Run GSC: Open Documentation and pick
one to read it in a Markdown preview:
- Language reference (
language-reference.md): the language, with runnable examples.
- Builtins (
builtins.md): every builtin function and where it runs.
- Deviations from Black Ops III (
deviations.md): where the language differs.
- GSC in Godot (
godot-interop.md): scripts on nodes, signals, exports, project settings,
installing by hand and exporting games.
- Debugging (
debugging.md): launch and attach configurations, breakpoints, hot reload and the
GSC Threads view.
- Diagnostics (
diagnostics.md): every GSCnnnn code the language server, the compiler and the
runtime report.
- The gsc command line (
cli.md): the bundled gsc.exe (in the extension's server\win32-x64
folder) for checking and running scripts outside Godot.
Building from source
From editors/vscode:
npm ci
npm run typecheck # strict tsc --noEmit: the lint (there is no ESLint)
npm run build # regenerates syntaxes/gsc.tmLanguage.json from the YAML, then bundles
npm test # grammar tests, scope coverage and snippet checks
npm run test:smoke # stdio test of build/tools-*/tools/lsp/gsc-lsp.exe
npm run test:integration # VS Code Insiders: fixture project, then tests/conformance (-- --label <name>)
npm run test:vsix # installs the newest VSIX into a throwaway profile and checks it there
npm run test:inspector:unit # GSC Threads view: model, contract, commands
npm run test:addon # GSC: Install Addon
npm run test:dap # debug adapter: units, scenarios against gsc.exe (GSC_CLI_PATH), Godot suites
npm run test:inspector # thread inspector: units and the agent oracle on gsc.exe and Godot
npm run test:lsp # the language-server wiring and gsc.regenerateGodotApi's pipeline
Every Godot these suites start (and test:demo's) gets its own APPDATA/LOCALAPPDATA under
build/ (build/vscode-test-appdata, build/vscode-demo/godot-appdata), never your
%APPDATA%\Godot; test:dap, test:inspector, test:lsp and test:integration fail when
anything below your %APPDATA%\Godot or %LOCALAPPDATA%\Godot changed during the run. Godot is GSC_GODOT, else the console exe that
node scripts/fetch-godot.mjs caches in build/cache/godot. docs/testing.md lists every suite.
- Grammar:
syntaxes/gsc.tmLanguage.yaml is the source; never edit the JSON.
npm run build:grammar regenerates it, and npm run test:grammar fails when it is stale.
- Grammar tests:
test/grammar/*.test.gsc use the vscode-tmgrammar-test format. ^ marks columns
of the line above and <- starts at column 0. test/grammar/check-scopes.mjs fails when a scope of
the grammar is not asserted anywhere.
To package the VSIX (Windows x64 only), from the repository root. node scripts/dev.mjs ci vscode runs
all of it, with the tests:
node scripts/dev.mjs build tools-release --target gsc_lsp,gsc_cli,gsc_apigen
node scripts/dev.mjs build godot-editor # the three addon DLLs (the addon zip needs all of them)
node scripts/dev.mjs build godot-debug
node scripts/dev.mjs build godot-release
node scripts/dev.mjs gen-api # build/api/godot-4.7.gscapi.json: the bundled Godot API data
node scripts/dev.mjs stage-vscode # gsc-lsp.exe, gsc.exe, gsc-apigen.exe, data/api, the user guides and the addon zip
cd editors/vscode && npm run package # gsc-godot-win32-x64-<version>.vsix
code --install-extension gsc-godot-win32-x64-0.1.0.vsix
stage-vscode takes another preset or --dir <buildDir> for a private tools tree and
--godot <editorTree,debugTree,releaseTree> for private Godot trees. The version is
package.json's, which must equal project(GSCGodot VERSION ...) in CMakeLists.txt (the DLLs report
that one to the extension; ci vscode checks both). Run from a checkout, the extension installs its
bundled zip (editors/vscode/addon/) when there is one, and says so; set gsc.addon.path to
hosts/godot/addon/addons/gsc to install the libraries built since. The Insiders tests
use GSC_LSP_PATH (default build/tools-debug/tools/lsp/gsc-lsp.exe); npm run test:vsix unsets it
so the installed extension runs its bundled server.
License
MIT. See the LICENSE file. Third-party notices are in THIRD_PARTY_NOTICES.
Trademarks
Call of Duty and Black Ops are trademarks of Activision Publishing, Inc. This extension is not
affiliated with, endorsed by or sponsored by Activision or Treyarch. It is an independent
implementation of the GSC scripting language and contains no Activision code or assets. Godot is a
trademark of the Godot Foundation; this extension is not affiliated with the Godot project.
| |