Pawn Studio — SA-MP / open.mpA complete Pawn environment for VS Code:
The extension is available in English and Português (Brasil). It follows the VS Code display
language, or you can force one with
Compile from any fileThe extension reads the How the target is chosen, in order:
A
A candidate included by another main file is not a main file, so ALS hook modules are never mistaken for gamemodes. "Pawn Studio" panelEvery compilation shows up in the Pawn Studio tab of the bottom panel (next to Problems, Output and Terminal):
The same tab also keeps the package manager log and the server log of debug sessions. The chips on top
filter by kind.
Live error lensErrors show up at the end of the line while you type, with no need to save. There are two layers:
Diagnostics follow the text: inserting lines moves them, and editing a line with an error clears it right away. Syntax and IntelliSense
Package manager (GitHub)It works like npm, with its own
A package doesn't need to have been written for Pawn Studio. When a repository has no
Search and installIn the Pawn sidebar, open Packages and click
When a package is installed
UpdatesThe view shows each package in one of these states:
Lock file and removal
GitHub limitsWithout signing in, GitHub allows 60 requests per hour and 10 searches per minute. Pawn: Sign in to GitHub uses the account already connected to VS Code and raises the limit to 5000 per hour. Creating
|
| Field | Required | Description |
|---|---|---|
name |
no | name of the project |
version |
no | version of the project (for your own reference) |
entry |
recommended | main .pwn file, relative to pawn.json |
output |
no | .amx to generate (default: next to the .pwn) |
dependencies |
no | { "user/repo": "version" }: GitHub packages (see the versions below) |
devDependencies |
no | same format; packages only needed during development (e.g. crashdetect) |
target |
no | which builds packages install (server, system, arch, variant), see Project settings |
compiler |
no | pawncc path, args and includes of this project |
supports |
packages only | servers the package is for: samp, openmp or both |
include |
packages only | folder(s) with the .inc files you export (optionally per server) |
plugins |
packages only | which release file holds the binaries, per system / arch / variant |
components |
packages only | the open.mp component file, when it is separate from plugins |
Versions
The key is the GitHub repository (user/repo). The value says which version to install.
| Value | Installs |
|---|---|
"^2.9.6" |
the newest 2.x.x version that is at least 2.9.6 (default when installing) |
"~2.9.6" |
the newest 2.9.x version (fixes only) |
"2.9.6" |
exactly 2.9.6 (the tag v2.9.6 or 2.9.6) |
"2.x", ">=1.2 <2", "^1 \|\| ^2" |
any version in the range |
"latest" |
the newest version (or the default branch if the repository has no tags) |
"tag:r1-final" |
a tag by its exact name, for tags that aren't version numbers |
"branch:dev" |
the newest commit of the branch (the commit is locked in pawn-lock.json) |
"commit:a1b2c3d" |
exactly that commit |
Tags in the v1.2.3 and 1.2.3 styles work, and so does the SA-MP style R41-4, which is read
as 41.4. Pre-releases (-beta, -rc1) are only installed when the version you ask for names one.
What to commit
- ✅
pawn.jsonandpawn-lock.json: anyone who clones the project gets the same versions with Install pawn.json dependencies. - ❌
.pawn-studio/: the downloaded packages. It is downloaded again from the lock, and it has its own.gitignore, so git skips it without you doing anything. (Projects from versions before 1.4 had adependencies/folder: the packages move to.pawn-studio/dependencies/on their own.)
Publishing your own package
Find your case below, copy its pawn.json to the root of your repository and name the release files
the same way. Then:
- commit the
pawn.jsonbefore creating the tag: installers read thepawn.jsoninside the tag (v1.0.0), not the one on your main branch; - create the GitHub release for that tag and attach the files;
- run Pawn: Check my package (Packages view → ⋯): it lists what SA-MP and open.mp get on each system, and warns about anything missing;
- add the
pawn-packagetopic to the repository, so it shows up in the search.
| Your package | Case |
|---|---|
only .inc files |
A |
| a SA-MP plugin (open.mp also loads it) | B |
| an open.mp component only | C |
| a plugin and a component, in the same file | D |
| a plugin and a component, in separate files | E |
one folder per server inside the archive (samp/…, open.mp/…) |
D2 |
| any other organization inside the archive | G |
| builds for several systems / 64-bit / dynamic | F |
A. Include only
No pawn.json and no release needed: push the repository and create version tags. The .inc files are
found on their own (an include/ folder, or the repository root). Add a pawn.json only to list
dependencies:
{ "dependencies": { "pawn-lang/samp-stdlib": "latest" } }
B. SA-MP plugin
Release files:
myplugin-1.0.0-win32.zip myplugin-1.0.0-linux.tar.gz
├─ plugins/myplugin.dll ├─ plugins/myplugin.so
└─ include/myplugin.inc └─ include/myplugin.inc
{
"supports": "samp",
"plugins": {
"windows": "myplugin-*-win32.zip",
"linux": "myplugin-*-linux.tar.gz"
}
}
| Server | Gets |
|---|---|
| SA-MP | plugins/myplugin.dll + plugins myplugin in server.cfg |
| open.mp | the same file as a legacy plugin, in pawn.legacy_plugins of config.json |
C. open.mp component only
mycomponent-1.0.0-win32.zip mycomponent-1.0.0-linux.tar.gz
├─ components/mycomponent.dll ├─ components/mycomponent.so
└─ include/mycomponent.inc └─ include/mycomponent.inc
{
"supports": "openmp",
"plugins": {
"windows": "mycomponent-*-win32.zip",
"linux": "mycomponent-*-linux.tar.gz"
}
}
| Server | Gets |
|---|---|
| open.mp | components/mycomponent.dll (components load on their own, no config) |
| SA-MP | nothing: the install log says the package is for open.mp only |
With "supports": "openmp", loose .dll/.so files at the root of the archive also count as components.
D. Plugin and component in the same file
One file per system, with both builds inside:
audio-cast-1.0.3-windows-x86.zip
├─ plugins/audio-cast.dll ← SA-MP
├─ components/audio-cast.dll ← open.mp
└─ include/audio-cast.inc
{
"supports": ["samp", "openmp"],
"plugins": {
"windows": "audio-cast-*-windows-x86.zip",
"linux": "audio-cast-*-linux-x86.zip"
}
}
| Server | Gets |
|---|---|
| SA-MP | plugins/audio-cast.dll + server.cfg (the component is skipped) |
| open.mp | components/audio-cast.dll (the plugin is skipped, so it never loads twice) |
D2. One folder per server inside the archive
Each server's files in a folder of its own, as they would be laid out in that server:
PawnJson-1.0.0-windows-x86.zip
├─ samp/
│ ├─ plugins/PawnJson.dll
│ └─ pawno/include/PawnJson.inc
└─ open.mp/
├─ components/PawnJson.dll
└─ qawno/include/PawnJson.inc
{
"supports": ["samp", "openmp"],
"plugins": {
"windows": "PawnJson-*-windows-x86.zip",
"linux": "PawnJson-*-linux-x86.zip"
}
}
SA-MP uses only the samp/ folder (plugin, server.cfg and its include); open.mp uses only open.mp/
(component and its include). Folder names recognized: samp, sa-mp, legacy and open.mp,
openmp, omp, at the root of the archive or one level down. With other folder names, declare them
with layout.
G. Declare where each file is: layout
When the archive is organized your own way, say where each kind of file is, per server. Paths are
relative to the archive root (a single top folder such as mylib-1.0/ is looked through):
mylib-1.0.0-win32.zip
└─ mylib-1.0.0/
├─ bin/legacy/mylib.dll
├─ bin/omp/mylib.dll
├─ inc/samp/mylib.inc
├─ inc/omp/mylib.inc
└─ libs/libcurl.dll
{
"supports": ["samp", "openmp"],
"plugins": { "windows": "mylib-*-win32.zip", "linux": "mylib-*-linux.tar.gz" },
"layout": {
"samp": { "plugins": "bin/legacy", "include": "inc/samp" },
"openmp": { "components": "bin/omp/mylib.dll", "include": "inc/omp" },
"all": { "libraries": "libs/*.dll" }
}
}
| Role | Goes to |
|---|---|
plugins |
plugins/ of the server, added to server.cfg / config.json |
components |
components/ |
libraries |
next to the server executable |
include |
the include path |
- Each value is a folder (
"bin/legacy"), a pattern ("bin/omp/*.dll") or a list of them. sampis used on SA-MP servers,openmpon open.mp servers,allon both.- Only the
.dll(Windows) or.so(Linux) of the system being installed are taken from those folders. - With
layout, only what it lists is installed: no detection. A path that is not in the archive is reported in the install log and by Pawn: Check my package.
E. Plugin and component in separate files
myplugin-1.0.0-win32.zip myplugin-omp-1.0.0-win32.zip
└─ plugins/myplugin.dll └─ components/myplugin.dll
{
"supports": ["samp", "openmp"],
"plugins": { "windows": "myplugin-*-win32.zip", "linux": "myplugin-*-linux.tar.gz" },
"components": { "windows": "myplugin-omp-*-win32.zip", "linux": "myplugin-omp-*-linux.tar.gz" }
}
SA-MP downloads only the plugins file; open.mp downloads only the components file. A file that a
components pattern matches is never used as the plugin, even when the plugins pattern matches it too.
F. Several systems, architectures and variants
Add one key per build. The plain windows / linux keys are the 32-bit builds, which is what SA-MP
and open.mp load:
{
"plugins": {
"windows": "mylib-*-win32.zip",
"windows-x64": "mylib-*-win64.zip",
"linux": "mylib-*-linux-static.tar.gz",
"linux-dynamic": "mylib-*-linux-dynssl.tar.gz"
}
}
The project that installs the package chooses the build with "target" in its own pawn.json (see
Project settings): { "os": "linux", "variant": "dynamic" }.
A variant the package does not have falls back to the plain key of that system.
Reference
| Field | Values |
|---|---|
supports |
"samp", "openmp" or ["samp", "openmp"] (default) |
plugins |
{ key: "file pattern" } — the file with the binaries (* = anything); false = no binaries; left out = chosen by the file names |
components |
same format; only when the open.mp component is a separate file |
include |
"include", ["a", "b"] or { "samp": "...", "openmp": "...", "all": "..." }; left out = detected |
layout |
where each kind of file is inside the archive, per server — see G |
dependencies |
packages yours needs, installed together with it. The sampctl list form (["user/repo", "user/repo:tag"]) works too |
Keys of plugins / components: windows, linux, windows-x64, linux-x64, and variants such as
linux-dynamic, linux-static or linux-x86-dynamic. A value can also be a list of patterns.
Inside the release file:
| Path | Goes to |
|---|---|
plugins/*.dll / plugins/*.so |
plugins/ of the server, added to server.cfg / config.json |
components/*.dll / components/*.so |
components/ (open.mp only) |
other .dll / .so (with a plugins/ folder present) |
next to the server executable (runtime libraries such as libmariadb.dll) |
folders with .inc files |
the include path, before the repository source |
samp/… and open.mp/… (one folder per server) |
only the folder of the project's server is used |
A pawn.json that already has sampctl fields (user, repo, include_path, resources) can keep them:
add the Pawn Studio fields next to them and the same file serves both tools. Archives inside the
release archive (a .tar.gz inside a .zip) are opened too.
When the release brings the .inc files, the repository source (SDKs, C++ code) is not kept in the
installer's .pawn-studio/dependencies/ folder.
Project settings: target and compiler
The pawn.json of your project (in the server folder) can hold settings that belong to the project,
so everyone who opens it gets the same:
{
"entry": "gamemodes/main.pwn",
"target": { "os": ["windows", "linux"] },
"compiler": {
"path": "pawno/pawncc.exe",
"args": ["-d3", "-;+", "-(+"],
"includes": ["pawno/include"]
}
}
target — which builds packages install:
| Key | Default | Example |
|---|---|---|
os |
the system of the server in the folder (or this machine) | ["windows", "linux"]: test on Windows, host on Linux — both the .dll and the .so go to plugins/ |
server |
detected from samp-server / omp-server |
"openmp" |
arch |
"x86" (what SA-MP and open.mp load) |
"x64" |
variant |
the default build | "dynamic", "static" |
With several systems, the config file (server.cfg / config.json) is written for the server in
the folder. A SA-MP server on Linux needs the .so in the name (plugins streamer.so): the install log
shows that line for the other system.
compiler — path of pawncc, args and extra includes (paths relative to pawn.json). They win
over the pawnStudio.compilerPath, pawnStudio.args and pawnStudio.includePaths settings.
Tools: objects, sprites, animations, sounds and dialogs
The Tools section of the Pawn sidebar opens seven tools. Every one inserts code at the cursor of the file you were editing.
The finders read models, textures, animations and sounds straight from your own GTA San Andreas
installation: nothing from the game is bundled with the extension. The game folder is detected on
its own (SA-MP's registry key, then common install folders). If it isn't found, pick it with
Pawn: Select the GTA San Andreas folder or the pawnStudio.gtaPath setting. To use another
copy of the game later, click Change folder at the top right of the finders, or the
GTA San Andreas item at the end of the Tools section (it shows the folder in use).
| Tool | What it does | Inserts |
|---|---|---|
| Objects | all objects from GTA and SA-MP (SAMP.ide), searchable by ID, model or texture, with a 3D textured preview. Filters for source, type (static, timed, animated), map usage, LODs and draw distance. Each object shows every spot where the game places it on the map. Results on the map: search a name (pump, atm…) and see where every object found is, with its real position and rotation. Map area: drag a rectangle on the San Andreas map to list every object inside it |
CreateObject, CreateDynamicObject, RemoveBuildingForPlayer (one object, every copy of a model, or a whole area), SetPlayerPos, and every position of a model as a Pawn array |
| Sprites | every TextDraw sprite in models/txd (ld_beat:chit, ld_tatt:9crown…), shown as images, with their size |
a global or player TextDraw already set to the sprite font and size |
| Animations | every animation library (PED, DANCING, BAR…), played in 3D on any skin, with loop, "in place" and speed controls |
ApplyAnimation(playerid, "LIB", "NAME", …) |
| Sounds | every sound ID with the game's own name (1139 · SOUND_CHECKPOINT_RED). The 🔊 ones play in the editor with the same bank sounds the game uses. The "All game sounds" view lets you listen to the ~62,000 sounds in audio/SFX and save any of them as .wav |
PlayerPlaySound(…) with the name as a comment |
| Attached Objects | 3D editor to position objects on the player (any skin, 18 bones, offset, rotation, scale and material colors) or on a vehicle (any vehicle and color). Move, rotate and scale with the mouse; object browser with thumbnails (hats, helmets, glasses, masks…) | SetPlayerAttachedObject, or CreateObject + AttachObjectToVehicle (CreateDynamicObject + AttachDynamicObjectToVehicle with the streamer) |
| GangZone Creator | draws gang zones on the San Andreas map: rectangles, circles and polygons (point by point). Move and resize them with the mouse, set the color and opacity, or type the coordinates | GangZoneCreate + GangZoneShowForPlayer/ForAll, and with the streamer CreateDynamicRectangle / CreateDynamicCircle / CreateDynamicPolygon with OnPlayerEnterDynamicArea |
| Dialog Creator | builds a dialog visually, in every style, with a preview that looks like the game, {RRGGBB} colors and the response handler |
ShowPlayerDialog + OnDialogResponse, or easyDialog Dialog_Show + Dialog:name |
Results on the map: every place where the objects you searched for are, with the real position and rotation.

Object preview with the game's own textures:

Attached Objects: position objects on the player or on a vehicle in 3D.

GangZone Creator: rectangles, circles and polygons, with streamer areas.

Dialog Creator: a preview that looks like the game.

Animations in 3D on any skin, and sounds that play in the editor:


- Dialog Creator details:
- With the cursor on an existing
ShowPlayerDialogorDialog_Show, the creator opens filled in with that dialog. It's also in the editor's right-click menu. - The code style follows your project: easyDialog when it already uses
Dialog_Show. - Long texts are built with
strcat, so no line goes over the compiler's limit.
- With the cursor on an existing
- Dialog text: type
\tand\nlike in Pawn, or press Tab in the text box. Both become columns inTABLISTdialogs. SA-MP shows at most 4 columns (3 tabs per row): the Dialog Creator warns about longer rows and leaves the extra columns out of the code. - Attached Objects: the editor comes from
SA-MP Preview Studio, with the models read from
your game. With the cursor on
SetPlayerAttachedObjectorAttachObjectToVehiclelines (or with them selected), run Pawn: Attached Objects or use the right-click menu: they open in the editor, and Insert all in the editor writes the result back. Shortcuts: W move, E rotate, R scale, F focus. - Custom skins and models: the game models are read the way the game loads them: first the
modloader/folder (loose.dff/.txd, and new skin ids from its.idefiles), then the extra IMGs indata/gta.dat, then the stock archives. So a skin replaced with modloader or insidegta3.imgshows up in place of the original, in the attachment editor and in the animation preview. The server's own models also appear, marked custom:AddCharModel(skins 20001+) andAddSimpleModel(objects, negative ids) frommodels/artconfig.txtor from the gamemode code. - Which sounds play: the link between a sound ID and the audio banks is built into
gta_sa.exe. The extension ships that table, rebuilt from the reverse-engineered game code (gta-reversed). Each ID is marked:- 🔊 the game plays it from its resident banks, so it is what players hear;
- 🔇 it needs a mission audio bank that a game script loads, so it is usually silent on SA-MP;
- 🎵 it starts a streamed music track;
- no mark: nothing audible.
- Map area: the map is the game's own radar image. Placements come from the game's IPL files
(text IPLs plus the binary stream IPLs inside
gta3.img). The generatedRemoveBuildingForPlayerlines include the LOD models, so buildings don't stay visible from far away. - Results on the map: click Results on the map under the search box. Every object the search and
filters find shows up on the map, one color per model. Click a model in the list to see only its
positions (as a table), or click a dot to get its coordinates,
CreateObjectwith the same rotation,RemoveBuildingForPlayerandSetPlayerPos. Insert the positions writes all of them as a Pawn array, handy to build a system around the game's own gas pumps, ATMs or doors. The rotation is converted from the IPL quaternion to therotX, rotY, rotZthatCreateObjectuses. - Map zoom: every map has a zoom bar on the right (+, slider, −, whole map), so the mouse wheel is not needed. Double-click also zooms in.
- GangZone Creator: SA-MP only draws rectangular gang zones, so circles and polygons become
horizontal strips that do not overlap (choose how many; the total is checked against the
1024 gang zone limit). The streamer areas follow the exact shape. The generated code has
Zones_Create(),Zones_Show(playerid)andZones_Hide(playerid)(or theForAllversions). With the cursor onGangZoneCreateorCreateDynamic*lines, run Pawn: GangZone Creator or use the right-click menu to open them in the editor; the code it generates opens again with the same shapes. Shortcuts: V select, R rectangle, C circle, P polygon, H move the map, Delete removes, Ctrl+D duplicates.
Debugging the server

F6, the ▶ button in the Server view, or Run and Debug → SA-MP / open.mp Server:
- compiles the gamemode (and stops if there are errors);
- warns if
server.cfgloads a different gamemode; - starts
samp-server.exe/omp-server.exeand streams the log to the Debug Console; - when crashdetect reports an error (array index out of bounds, native failed, hang…):
- VS Code stops at the exact line with the error message and a hint;
- the call stack shows each function with its file and line;
- the Variables view shows each function's arguments (
playerid = 0,cmdtext = "/test"); - the server keeps running: Continue (F5) moves on;
- the Debug Console REPL sends commands to the server: via RCON on SA-MP (needs
rcon_password), via the console on open.mp.
SA-MP doesn't support breakpoints or stepping. The debugger shows where and why an error happened. Install crashdetect (search "crashdetect" in Packages) and keep
-d3in the compiler arguments.
"Pawn" sidebar
- Compilation:
- result of the last compilation or check, with duration and time;
- target and
.amxsize; - clickable errors and warnings;
- compiler and include folders.
- Packages: direct and indirect dependencies with their state (installed, up to date, update available, missing), plugins and include paths, with inline actions.
- Server:
- running or stopped;
- executable (SA-MP or open.mp),
server.cfg, gamemode and port; - server log;
- plugins, warning when a file is missing from
plugins/; - history of runtime errors with a clickable call stack.
Settings
| Setting | Default | Description |
|---|---|---|
pawnStudio.language |
auto |
extension language (auto, en, pt-br) |
pawnStudio.compilerPath |
"" |
path to pawncc (empty = automatic) |
pawnStudio.includePaths |
[] |
extra include folders |
pawnStudio.args |
["-;+", "-(+", "-d3"] |
compiler arguments |
pawnStudio.outputDirectory |
"" |
.amx folder |
pawnStudio.entry |
"" |
pinned target |
pawnStudio.saveBeforeCompile |
true |
save before compiling |
pawnStudio.lint.quick |
true |
quick analysis while typing |
pawnStudio.lint.onType |
true |
background pawncc without saving |
pawnStudio.lint.delay |
1500 |
idle ms before calling pawncc |
pawnStudio.lintOnSave |
true |
background check on save |
pawnStudio.completion.parameters |
required |
parameters inserted by autocomplete |
pawnStudio.revealOutput |
onError |
when to show the output |
pawnStudio.errorLens.* |
— | error lens (enable, warnings, other languages) |
pawnStudio.server.path |
"" |
samp-server.exe / omp-server.exe |
pawnStudio.packages.checkUpdatesOnStartup |
true |
check updates when opening |
pawnStudio.gtaPath |
"" |
GTA San Andreas folder for the tools (empty = automatic) |
pawnStudio.dialogs.codeStyle |
auto |
Dialog Creator code: auto, native, easy |
Development
npm install
npm test # unit + grammar + translation tests
npm run test:live # real GitHub integration (installs crashdetect + streamer in a temp folder)
npm run test:vscode -- <pawncc.exe> <include-folder>... # runs everything inside a real VS Code
npm run package # builds the .vsix
Translations
- Code messages use
t('English text {0}', value)(src/i18n.ts). Translations live inl10n/bundle.l10n.<locale>.json. - Menus and settings use
package.nls.json/package.nls.<locale>.json. npm testfails if a message has no translation or the placeholders don't match.- To add a language, copy the
pt-brfiles, translate them, and add the locale to thepawnStudio.languagesetting.


