Install-Layout Language
The install-layout language is how you, a plugin developer, teach RPH Log Analyzer what a working install of your plugin looks like. You write a few lines when registering a release (Settings → Developer → Register release); the analyzer then verifies every player's install against them and names exactlywhat's wrong — the missing config file, the folder they forgot to extract, the leftover DLL from an old version that's breaking everything.
It is not a scripting language. Every statement compiles to a simple existence check inside the player's GTA folder. Nothing you write here can execute, read file contents, or touch anything outside the game directory — which is why the analyzer can run it against thousands of installs safely.
The language is part of the Diagnostics Ready capsule format.
Thirty-second version
# a working install of MyPlugin
require plugins/LSPDFR/MyPlugin.ini "your settings file" # must exist
require plugins/LSPDFR/MyPlugin/ # trailing / = folder must exist
optional plugins/LSPDFR/MyPlugin/sounds/ # nice to have - never an error
forbid scripts/MyPlugin.dll "the old 1.x location - it double-loads"
plugins/LSPDFR/MyPlugin.xml # bare path = require, for the lazyOne statement per line. # starts a comment. A trailing "note" is a short phrase players get to see. Keywords are case-insensitive. That's most of it.
The editor shows you the payoff live
The register page's layout editor is a tiny IDE for this language: keywords, paths, notes and comments are syntax-highlighted as you type, every problem is flagged on its exact line (all of them at once, not just the first), and a "What players will see" panel below the editor renders the exact plain-English install guide your lines compile into — sections for Put these in place, Optional extras and Old files to delete, grouped by destination folder, with your notes beside each file. If a line reads poorly in the preview, players will find it confusing too; fix it before you submit.
Statements
require <path> — this must exist
The bread and butter. When the path is missing from a player's install, their crash report says so by name:
Installation check: plugins/LSPDFR/MyPlugin.ini — the developer says a working install includes these. Reinstall the plugin or restore them.And when the missing file is actually sitting somewhere else inside the GTA folder — the classic "extracted the zip into the wrong folder" install — the analyzer notices and tells the player to move it instead:
You haveMyPlugin.dllin the main GTA V folder — the install instructions say it goes atplugins/LSPDFR/MyPlugin.dll. Move it there.
This wrong-place detection is automatic — one require line buys it. You never need forbid lines enumerating the wrong spots a file might land in; the analyzer probes the GTA folder tree itself (the root, the load folders, extracted-zip subfolders, even the top of x64/ and mods/) and names the actual location it found.
Use it for every file your plugin cannot run without: your config, your data folder, the XML your callouts read — and your DLLs' paths. The release's file hashes already prove a DLL is genuine and uncorrupted, but only a require line pins where it belongs (require plugins/LSPDFR/MyPlugin.dll). That placement line is also what powers the "How to install" guide on your public listing — without it, the guide can't tell a player where your plugin actually goes.
optional <path> — this may exist
For things a working install caninclude but doesn't have to: an optional sounds pack, an extra language file. An absent optional path is never reported as a problem; it's informational only. Use it sparingly — its main value is documenting the full shape of your install for future you.
forbid <path> — this must NOT exist
The one that saves the weirdest support tickets. When the path is present, the report says:
Leftover files: scripts/MyPlugin.dll — the developer says these must be removed for the plugin to work (old versions or moved files). Delete them.Use it when:
- you moved your plugin between folders (
scripts/→plugins/LSPDFR/) and stale copies double-load or shadow the new one; - you renamed a DLL and the old name still floating around causes conflicts;
- an old data file, if present, gets read instead of the new one.
Don't use it for "the current file in the wrong folder" — require already finds misplaced copies automatically and tells the player to move them. forbid is for paths that must not exist at all, even alongside a perfectly correct install.
Bare path — shorthand for require
A line that is just a path behaves exactly like require. Handy for quick lists; prefer the explicit keyword once you're also using optional or forbid, so the intent stays readable.
One guard rail: a bare path whose name starts with a keyword — forbidplugins/BurnsCommon.dll, requirements.txt — is rejected as a probable missing space, because silently reading it as require forbidplugins/… would invert your intent. The error shows both fixes: add the missing space (forbid plugins/BurnsCommon.dll), or, for a file genuinely named that way, spell the rule out (require requirements.txt). The explicit form always works.
"note" — say what it is, in your words
Any statement can end with a short double-quoted phrase:
require plugins/LSPDFR/MyPlugin.ini "your settings - regenerated on first run"
forbid scripts/MyPlugin.dll "the old 1.x location - it double-loads"Unlike a # comment (which stays with you), a note ships in the compiled entryand surfaces everywhere the plain-English text does. The player's report goes from
Installation check: plugins/LSPDFR/MyPlugin.ini — the developer says a working install includes these.to
Installation check: plugins/LSPDFR/MyPlugin.ini (your settings - regenerated on first run) — the developer says a working install includes these.and your public listing's install guide can say what each file is, not just where it goes. The guide names each destination folder once and lists your files under it with their notes:
In the "plugins/LSPDFR" folder:MyPlugin.ini— your settings - regenerated on first run
Rules: one phrase, max 120 characters, straight quotes ("), the note goes last on the line (a # comment may still follow it). Notes are where forbid earns its keep — one clause about why the file must go turns a bare deletion order into an explanation players actually follow.
Comments and blank lines
# starts a comment, either on its own line or after a statement. Blank lines are ignored. Comment generously — the layout doubles as documentation of your own install shape.
require plugins/LSPDFR/MyPlugin/data/ # vehicles.xml, peds.xml live hereOne deliberate subtlety: a # only starts a comment at the start of a line or after a space — and never inside a "note" (require x.ini "the #1 config" keeps its note whole). Glued to text (plugins/My#1.ini) it stays part of the name — and since #isn't a legal path character, you get a clear error instead of half your path being silently swallowed. The language never throws away something you typed without telling you.
Paths
- Always relative to the GTA V root — the folder with
GTA5.exein it. Never absolute, never a drive letter. /and\both work; they mean the same thing. (Compiled capsules always store/, so it doesn't matter which your muscle memory prefers.)- A trailing / marks a folder — the check then requires a directory with that name. Without it, the check requires a file.
require x/is not satisfied by a file namedx, and vice versa. Be deliberate about it — declaring the same path once as a file and once as a folder is an error that names both lines. - Case doesn't matter (Windows filesystems don't care, so neither do we).
- Spaces are fine inside names:
require plugins/LSPDFR/My Plugin.ini.
Path rules the compiler enforces
| Rule | Why |
|---|---|
| Relative only — no C:\, no leading /, no UNC | checks must stay inside the game folder |
| No .. anywhere | same — no escaping the GTA root |
| Max 6 folder segments deep, max 180 characters | real plugin layouts fit comfortably; hostile ones don't |
| Letters, digits, spaces, ., _, - per segment | no control characters, no : streams, no tricks |
| No Windows reserved names (CON, NUL, COM1…) | they behave bizarrely as filenames |
| No duplicate paths in one layout | each path gets exactly one rule |
| Max 64 statements | a layout is a shape, not a manifest of every asset |
Anything off-spec is rejected when you register, with the line number, the offending text, and — when the fix is guessable — a suggestion:
Line 3: unknown keyword 'requre' - did you mean 'require'?
Line 4: 'require' needs a path after it, e.g. 'require plugins/LSPDFR/MyPlugin.ini'.
Line 9: 'plugins/X.ini' is already marked 'require' on line 2 - a path can't be both require and forbid.
Line 6, position 8: an invisible zero-width character (common after copy-paste) - delete and retype that spot.Conflicts always name both lines involved. Invisible characters smuggled in by copy-paste (non-breaking spaces, zero-width characters, curly quotes) are caught by exact position instead of failing later with something baffling. Nothing invalid ever reaches players.
Reserved keywords
ini, hash, version, edition and registryare reserved for future statements (think: checking a key inside your config file, or gating on game edition). Today they produce an error instead of being read as a bare path — that's intentional, so a layout written for a future version of the language can never be silently misread by an older app.
How verification actually runs
- You register a release; the layout text compiles into data entries inside your signed
.rphlacapsule. The text itself never ships — only path + rule (+ your"note", if you wrote one) entries do. - A player with your capsule installed runs a log analysis.
- For a capsule whose signature and file hashes check out, the analyzer walks your entries: existence check per path,
forbidinverted, everything read-only. - Problems appear on your plugin's card in their report, named exactly, with your official download and support links right beside them.
Two properties worth knowing:
- Layout findings never demote your release's verified status. Hashes matching and install shape are reported as separate facts — "genuine release, incomplete install" is precisely the diagnosis you want players to see.
- Checks only run for validly signed capsules. Nobody can ship a fake capsule that "verifies" their tampered copy of your plugin against a fake layout.
A complete real-world example
# ---- SuperCallouts 3.1 install layout ----------------------------
require plugins/LSPDFR/SuperCallouts.ini "your settings - regenerated on first run"
# callout data
require plugins/LSPDFR/SuperCallouts/ "all the callout data lives in here"
require plugins/LSPDFR/SuperCallouts/callouts.xml
optional plugins/LSPDFR/SuperCallouts/audio/ "the optional voice pack"
# we moved out of scripts/ in 2.0
forbid scripts/SuperCallouts.dll "the pre-2.0 location - it double-registers callouts"
forbid scripts/SuperCallouts.iniGood practice
- Always require your DLLs' paths. Hashes prove the bytes; the
requireline pins the location and teaches the public install guide where your plugin goes. It's the one line every layout should have. - Then start small. DLL paths, your config file, your data folder — that catches the majority of broken installs. Add more only when a support ticket teaches you a new failure shape.
- Add a forbid every time you move or rename something. Future updates are exactly when leftovers appear.
- Put a "note" on anything a player might not recognize. "your settings file" beats a bare
.inipath; aforbidwith why("it double-loads") gets deleted, a bare one gets a support ticket. Skip notes on self-explanatory paths — they should earn their place. - Don't list every asset.If one missing texture among 400 doesn't break the plugin, it doesn't belong in the layout. The 64-statement cap is a feature.
- Test it on yourself: register, drop the signed
.rphlanext to your DLL, delete your own config, run an analysis. You should see your plugin's card call it out.
Errors you might see while writing
| Message | Meaning |
|---|---|
Line N: unknown keyword 'x' - did you mean 'require'? | keyword typo — the suggestion is usually right |
Line N: unknown keyword 'x' - use require, optional or forbid (or a bare path) | a bare word that isn't a keyword or a path |
Line N: 'require' needs a path after it, e.g. … | keyword with nothing following |
Line N: 'optional' right after 'require' reads like a second keyword - write one rule per line | two keywords on one line (a folder literally named optional still works: require optional/) |
Line N: missing a space after 'forbid'? Write 'forbid plugins/…' | keyword glued to its path (forbidplugins/…) — or a bare name starting with a keyword, which needs an explicit rule |
Line N: the '?' prefix isn't part of the layout language - write 'optional <path>' | old pre-release syntax |
Line N: 'ini' is reserved for a future version of the language | reserved future keyword |
Line N: '<path>' already appears on line M - each path is listed once | exact duplicate |
Line N: '<path>' is already marked 'require' on line M - a path can't be both require and forbid | conflicting rules for one path |
Line N: '<path>' conflicts with '<path>' on line M - it can't be both a file and a folder | same path with and without the trailing / |
Line N: unclosed note - add the closing " at the end of the line | a " without its partner |
Line N: a note can't contain " - keep it to one quoted phrase … | quotes inside the note text |
Line N: nothing can follow a note - the closing " ends the statement | text after the note (a # comment is fine) |
Line N: the note is empty / too long … | "", or past the 120-character cap |
Line N: a note needs a statement before it … | a quoted phrase on a line by itself |
Line N, position P: a non-breaking space / zero-width character / curly quote … | invisible copy-paste artifacts, located exactly (notes use straight quotes) |
Line N: Bad install path. / Bad install path segment. | the path breaks one of the rules above |
Line N: Install path too deep. | more than 6 folders |
Line N: too many entries (max 64). … | trim the layout to what actually breaks installs |
The language is versioned with the capsule format (rphla.capsule/1). New statements will land under the reserved keywords; layouts you write today keep working as-is.
Still need help? Contact support.