This guide explains how the C++ extension is built from a vendored upstream repository, how to reconstruct the tree, and how to update to newer upstream versions.
The C++ extension is built from two source trees, compiled together into a
single sp-debugger.ext.so:
sp-debugger/
sp-console-debugger/ # upstream peace-maker/sp-console-debugger + our patch -- NOT committed,
# reconstructed at build time (gitignored)
dap/ # our "crate": all DAP/TCP/profiler code + the glue extracted from upstream
patches/
sp-console-debugger.commit # the pinned upstream commit (one line, full SHA)
sp-console-debugger.patch # the residual divergence of the vendored copy from upstream
AMBuildScript / AMBuilder / PackageScript / configure.py # build, driven from the repo ROOT
dap/ talks to the upstream code through a single seam header, dap/dap-seam.h.
The only edits inside sp-console-debugger/ are the thin hook calls into that
seam (plus the pre-existing feature divergence this fork already carried). Those
edits are captured in patches/sp-console-debugger.patch, and the repo carries
only our own code plus that patch -- the upstream tree itself is fetched on
demand.
CI and the VS Code build tasks do this automatically (the "Fetch Debugger Source" task runs before configure and is a no-op when the folder is present). By hand, from the repo root:
git init sp-console-debugger
git -C sp-console-debugger fetch --depth 1 https://github.com/peace-maker/sp-console-debugger "$(cat patches/sp-console-debugger.commit)"
git -C sp-console-debugger reset --hard FETCH_HEAD
git -C sp-console-debugger apply --whitespace=nowarn ../patches/sp-console-debugger.patchThe result is byte-identical on every machine: pinned upstream commit + patch.
frame-utils.h is created by the patch, so its presence is the "fetched AND
patched" sentinel the build guard (AMBuildScript) and the fetch task check.
The folder keeps its own .git pointing at upstream -- that is intentional; it
is what makes regenerating the patch a one-liner (below). The root repo ignores
the whole folder.
| Concern | Location |
|---|---|
| DAP request handlers, TCP server, JSON, session manager | dap/ (always was ours) |
DapBridge (TCP/profiler ownership + all SendDAP*/profiler methods) |
dap/dap-bridge.{cpp,h} |
Break-loop wait / resume (OnBreak, NotifyResume) |
dap/dap-bridge.cpp |
| Conditional-breakpoint + logpoint evaluation | dap/breakpoint-eval.cpp |
| DAP variables-view value rendering | dap/symbol-render.cpp |
| The seam the upstream files call | dap/dap-seam.h |
The upstream files include only dap-seam.h from the crate; the dependency
direction is always dap/ -> sp-console-debugger/, never the reverse.
Edit the files in sp-console-debugger/ directly, build, test -- then refresh
the patch so a fresh reconstruction reproduces your change. Thanks to the
nested upstream repo the fetch leaves behind:
git -C sp-console-debugger add -A
git -C sp-console-debugger diff --cached > patches/sp-console-debugger.patchRound-trip check (delete + refetch must rebuild your exact tree):
mv sp-console-debugger /tmp/spcd-check
# ...run the reconstruction commands above...
diff -r -x .git -x mock /tmp/spcd-check sp-console-debugger # should print nothing- Put the new upstream commit SHA in
patches/sp-console-debugger.commit. - Delete
sp-console-debugger/and re-run the reconstruction. Ifgit applyfails (upstream changed a line the patch also touches), apply with--3wayinstead and resolve the conflict markers by hand -- the seam hooks are small and well-commented (search fordap::):git -C sp-console-debugger apply --3way ../patches/sp-console-debugger.patch
- Regenerate the patch (see above) once it applies cleanly.
- Rebuild and run tests:
.venv/bin/python configure.py --enable-optimize --targets=x86,x86_64 --sm-path=/path/to/sourcemod .venv/bin/ambuild objdir (cd vscode && bun test) # unit suite vscode/tests/dap/run_dap_tests.sh # DAP integration/regression suite
- Commit the updated
patches/sp-console-debugger.commit+.patchpair.
sp-console-debugger/tests/setup.sh assembles what both regression suites run
against -- hl2sdk-mock, Metamod:Source and SourceMod, the last one built against
the patched SourcePawn VM (peace-maker's debug_api_symbols branch). All four
trees are pinned to a commit at the top of that script.
They must be pinned together. The patched VM forked from SourcePawn in
Nov 2024 and has not followed upstream since, while SourceMod master has:
master's core/logic/AMBuilder now calls SP.AddVmFlags() ("Statically link
SourcePawn", #2459, 2026-05-14) and master's core uses VM APIs added after the
fork (IPluginContext::LocalToArrayPtr, GetArrayData). Neither exists in the
fork, so tracking master dies at configure time with:
AttributeError: 'SourcePawn' object has no attribute 'AddVmFlags'
The mock env therefore follows the SourceMod 1.12 stable line, which is
contemporaneous with the fork and is the line the extension's ext-API-8 code
path targets (the SMINTERFACE_EXTENSIONAPI_VERSION guard in
dap/dap-bridge.cpp). Moving to a newer SourceMod line requires rebasing the
patched VM onto current SourcePawn first -- and note that master's static
linking also means there is no drop-in sourcepawn.jit.x86.so to ship there.
To bump a pin, edit the SHA in setup.sh and re-run everything:
sp-console-debugger/tests/setup.sh # rebuilds the trees at the new pins
sp-console-debugger/tests/run_tests.sh # console regression
vscode/tests/dap/run_dap_tests.sh # DAP regressionThen refresh patches/sp-console-debugger.patch -- setup.sh lives in the
vendored tree, so the pins live in the patch. CI keys its mock-env cache on the
hash of setup.sh, so a moved pin invalidates that cache by itself.
The build lives at the repo root (it compiles sp-console-debugger/*.cpp and
dap/*.cpp into one .ext.so; the build files inside sp-console-debugger/
are upstream-pristine and unused):
cd sp-debugger
# reconstruct sp-console-debugger/ first if missing (see above)
SOURCEMOD=/path/to/sourcemod .venv/bin/python configure.py \
--enable-optimize --targets=x86,x86_64 --sm-path=/path/to/sourcemod
.venv/bin/ambuild objdir
# -> objdir/package/addons/sourcemod/extensions/{,x64/}sp-debugger.ext.so
# (packaged with the matching sp-debugger.autoload)This is the same flow the VS Code tasks "Fetch Debugger Source" / "8. Configure Extension" / "9. Build Extension" run, with the repo root as the working directory.
See Building section in README.md for the SourceMod / patched-VM prerequisites; the DAP build is otherwise unchanged.