A GameAP plugin that adds structured, labelled editors for game-server config files to the panel. Instead of hand-editing raw config (and risking a typo that resets the server to defaults), you get grouped form fields - per game, format-aware, round-tripping every key it doesn't surface.
Started as a Palworld-only editor; now covers many games through a small set of shared config-format parsers.
38 of the 41 games in GameAP's built-in catalog, plus seven added manually.
game_id is what the plugin matches on (server.game_id); the server app id is
the Steam dedicated-server app from GameAP's own catalog, handy when adding a game
to the panel.
| Game | game_id |
Server app id | Config path |
|---|---|---|---|
| Palworld | palworld |
- | /Pal/Saved/Config/LinuxServer/PalWorldSettings.ini |
| Minecraft (Java) | minecraft |
- | /server.properties |
| Minecraft (Bukkit) | minecraft |
- | /bukkit.yml |
| Minecraft (Spigot) | minecraft |
- | /spigot.yml |
| Minecraft (Paper) | minecraft |
- | /config/paper-global.yml |
| Minecraft (operators) | minecraft |
- | /ops.json |
| Minecraft (whitelist) | minecraft |
- | /whitelist.json |
| Minecraft: Bedrock | minecraft-bedrock |
- | /server.properties |
| Minecraft: Bedrock (allow list) | minecraft-bedrock |
- | /allowlist.json |
| Minecraft: Bedrock (operators) | minecraft-bedrock |
- | /permissions.json |
| ARK: Survival Evolved | ark |
376030 |
/ShooterGame/Saved/Config/LinuxServer/GameUserSettings.ini |
| ARK: Survival Evolved | ark |
376030 |
/ShooterGame/Saved/Config/LinuxServer/Game.ini |
| Project Zomboid | projectzomboid |
- | /Zomboid/Server/servertest.ini |
| V Rising | 1604030 |
- | /save-data/Settings/ServerHostSettings.json |
| V Rising | 1604030 |
- | /save-data/Settings/ServerGameSettings.json |
| Enshrouded | enshrouded |
2278520 |
/enshrouded_server.json |
| RuneScape: Dragonwilds | rsdw |
4019830 |
/RSDragonwilds/Saved/Config/{Linux,LinuxServer,Windows,WindowsServer}/DedicatedServer.ini |
| 7 Days to Die | 7d2d |
294420 |
/serverconfig.xml |
| The Forest | the-forest |
556450 |
/Server.cfg |
| Hurtworld | hurtworld |
405100 |
/autoexec.cfg |
| Reign Of Kings | rok |
344760 |
/Configuration/ServerSettings.cfg |
| Factorio | factorio |
- | /server-settings.json |
| Game | game_id |
Server app id | Config path |
|---|---|---|---|
| Counter-Strike 2 | cs2 |
730 |
/game/csgo/cfg/server.cfg |
| Counter-Strike: GO | csgo |
740 |
/csgo/cfg/server.cfg |
| Counter-Strike: Source | cssource |
232330 |
/cstrike/cfg/server.cfg |
| Counter-Strike: Source v34 | cssv34 |
232330 |
/cstrike/cfg/server.cfg |
| Team Fortress 2 | tf2 |
232250 |
/tf/cfg/server.cfg |
| Garry's Mod | garrysmod |
4020 |
/garrysmod/cfg/server.cfg |
| Left 4 Dead 2 | l4d2 |
222860 |
/left4dead2/cfg/server.cfg |
| Left 4 Dead | l4d |
222840 |
/left4dead/cfg/server.cfg |
| Day of Defeat: Source | dods |
232290 |
/dod/cfg/server.cfg |
| Half-Life 2: Deathmatch | hl2mp |
232370 |
/hl2mp/cfg/server.cfg |
| Black Mesa: Deathmatch | bms |
346680 |
/bms/cfg/server.cfg |
| Synergy | synergy |
17525 |
/synergy/cfg/server.cfg |
| Game | game_id |
Server app id | Config path |
|---|---|---|---|
| Half-Life 1 | valve |
90 |
/valve/server.cfg |
| Counter-Strike 1.6 | cstrike |
90 |
/cstrike/server.cfg |
| Counter-Strike 1.5 | cs15 |
- | /cstrike/server.cfg |
| Counter-Strike: Condition Zero | czero |
90 |
/czero/server.cfg |
| Day of Defeat | dod |
90 |
/dod/server.cfg |
| Team Fortress Classic | tfc |
90 |
/tfc/server.cfg |
| Half-Life: Opposing Force | op4 |
90 |
/gearbox/server.cfg |
| Deathmatch Classic | dmc |
90 |
/dmc/server.cfg |
| Ricochet | ricochet |
90 |
/ricochet/server.cfg |
| Sven Co-op | svencoop |
276060 |
/svencoop/server.cfg |
| Sven Co-op (map defaults) | svencoop |
276060 |
/svencoop/default_map_settings.cfg |
GoldSource keeps server.cfg in the mod folder root. The cfg/ subfolder is a
Source-engine convention and no HLDS mod ships that directory - checked against
stock SteamCMD installs of app 90 (all seven mods) and app 276060. Ricochet
ships no .cfg files at all, so its server.cfg only exists once you create
one.
| Game | game_id |
Server app id | Config path |
|---|---|---|---|
| Quake 2 | q2 |
- | /baseq2/server.cfg |
| Quake 3 | q3 |
- | /baseq3/server.cfg |
| Call of Duty 4 | cod4 |
- | /main/server.cfg |
| FiveM | fivem |
- | /server.cfg |
| Game | game_id |
Server app id | Config path |
|---|---|---|---|
| Arma 3 | arma3 |
233780 |
/server.cfg |
| Arma 2 | arma2 |
33905 |
/server.cfg |
| Arma 2: Operation Arrowhead | arma2oa |
33935 |
/server.cfg |
| Game | game_id |
Server app id | Config path |
|---|---|---|---|
| TeamSpeak 3 | teamspeak3 |
- | /ts3server.ini |
| GTA: San-Andreas Multiplayer | samp |
- | /server.cfg |
| GTA: Multi Theft Auto | mta |
- | /mods/deathmatch/mtaserver.conf |
Some paths are conventions rather than guarantees: Arma loads whatever -config
names (and nothing if the argument is absent), the idTech engines resolve
server.cfg against their base directory, and The Forest honours
-configfilepath. Those entries say so in the error banner if the file isn't
there, and the file-manager editor still matches the file wherever it lives.
Where a path varies in a knowable way, an entry can list alternates in
altDirs instead of guessing. The tab tries each directory in turn, keeps the
first that answers, and pins it for that file - the pre-save conflict read, the
upload and the post-save verification all use the directory the content came
from. Saving to the first candidate after loading from a later one would write a
second config into a folder the server never reads, so the edit would look saved
and do nothing. If none of them has the file, the error names every path tried.
| Game | Why |
|---|---|
| Rust, Valheim | settings are launch arguments, not a file - use the Launch Settings tab |
| Just Cause 2 | config.lua is a Lua table; out of scope for the same reason as Project Zomboid's SandboxVars.lua |
Any game whose config matches a known format also works via the generic editor
even without a curated schema - keys are parsed, typed, and grouped by section,
with a raw-text fallback when a file doesn't parse. Hurtworld is registered that
way on purpose: only servername is well documented, so the editor lists what
the file actually holds rather than inventing keys.
Manual-add games: Palworld, Project Zomboid, V Rising, Factorio, Enshrouded and Minecraft: Bedrock aren't in GameAP's catalog - they're added by hand, so their
game_idis whatever your panel uses. Palworld is assumedpalworld, Project Zomboidprojectzomboid, Factoriofactorio, Enshroudedenshrouded, Dragonwildsrsdw, Bedrockminecraft-bedrock, and V Rising1604030(the game's Steam app id, which is how it was added here - not the dedicated-server app1829350). If your server uses a different code, the "Game Config" tab prints the actual one - change the matchinggameIdinfrontend/src/games/registry.ts.
GameAP has one Minecraft entry, and the panel can't tell a Paper server from a
vanilla one, so every Java file is offered on any minecraft server and the
error banner explains which server software writes each. bukkit.yml needs
CraftBukkit/Spigot/Paper, spigot.yml needs Spigot/Paper, and
config/paper-global.yml is Paper 1.19+ (older builds used paper.yml in the
server root; per-world settings live in config/paper-world-defaults.yml,
which isn't curated). All three are read at startup - restart after saving.
ops.json / whitelist.json (and Bedrock's allowlist.json /
permissions.json) are lists of players rather than settings, so they get no
curated schema: the generic editor renders one group per entry, and existing
entries can be edited in place. Adding or removing players stays a job for
/op, /deop and /whitelist, because the running server rewrites these files
whenever the list changes - the editor warns before saving to one.
Java and Bedrock both call their main config server.properties and share
almost none of its keys. With a game_id each resolves to its own schema; when
the file manager offers no game code, the plugin declines to guess and gives a
generic editor rather than labelling a Bedrock config with Java's fields.
enshrouded_server.json keeps its access control in a userGroups array - one
object per role, each with a password and five permission flags, and the password
a player types decides which role they join as. That array is the part hosts
edit most, so this file is parsed with the array-walking JSON format (the one
Minecraft's player lists use) rather than the plain one: each role becomes its
own userGroups[0], userGroups[1] group of typed fields instead of a single
input holding the whole array as one line of JSON.
The trade-off is at the other end. An empty array contributes no addresses, so
tags and the ban list are invisible on a fresh server - they round-trip
untouched, but adding the first entry needs the file manager's plain text
editor. Existing entries are editable in place either way.
Everything under gameSettings is only read when gameSettingsPreset is
"Custom"; under the other four presets the server uses the preset's values and
ignores the file's. The editor says so in a banner, because the edit otherwise
saves cleanly and changes nothing.
DedicatedServer.ini is an ordinary Unreal INI, with one trap. Jagex's guide
spells the settings section [/Script/Dominion.DedicatedServerSettings]; the
server writes [/script/dominion.dedicatedserversettings]. Matched
case-sensitively, every curated field would come up empty and saving would
append a second section that the game then ignores - so this file gets the
same case-insensitive INI treatment as ARK, covering the section name as well as
the keys.
Its one boolean is written Public=1 rather than Unreal's usual True/False,
so the format also counts 1/yes/on as true. Writes stay True/False,
which is what Unreal's own config writer emits and its reader accepts either
way.
Jagex documents five settings; all five are curated, plus Public, ServerGuid
(labelled as generated and not to be edited) and bCanSaveAllSections. That last
one lives in [SectionsToSave] rather than the settings section, but is shown
under Server / Identity because it governs whether the server rewrites this file
at all - the thing most likely to eat an edit made here. A schema group can mix
sections freely: the address carries its section with it. There is no
player-limit key (the server is capped at 6 and takes no setting for it), no
difficulty, and no ports - 7777/7778 UDP come from the command line. Edits made
while the server is running are lost, because it rewrites this file itself, so
the tab warns before saving to a running server.
The platform folder is not knowable up front, so the entry lists all four in
altDirs and the tab probes them: every source disagrees (XGamingServer
documents Linux, Jagex's own guide LinuxServer), and a Windows build run
under Proton writes the Windows folders even on a Linux node.
A GameAP plugin is a single .wasm file with two parts:
main.go- a thin Go/WASM shell implementingPluginService. It only reports plugin info and hands the panel the compiled frontend bundle. It uses no filesystem/server-control host calls - the panel reads and writes the files for us.frontend/- a Vue 3 + Vite bundle. All the logic lives here:
src/
formats/ parse/serialise per format, shared ConfigDoc contract
types.ts ConfigDoc / Codec / Format interfaces, ConfigValue
shared.ts codec factory, section-address encoding, the ordered
address table + line splitting every parser builds on
palworld.ts OptionSettings=(...) one-liner
keyvalue.ts flat key=value (Minecraft, PZ, Terraria)
ini.ts multi-section INI, optional case-insensitivity (ARK)
convar.ts console convars (Source/GoldSource/idTech, SA-MP variant)
json.ts JSON object, dotted paths (V Rising); an opt-in list mode
walks into arrays for Minecraft's player lists
yaml.ts block-style YAML, dotted paths (Bukkit/Spigot/Paper)
xml.ts XML, attribute- or element-valued (7d2d, MTA)
arma.ts Arma `key = value;` with quoted strings and arrays
*.test.ts round-trip / fidelity tests
games/
registry.ts game_id -> { file, dir, format, schema?, guardrails }
source.ts Source-engine entries (shared convar schema)
goldsource.ts GoldSource/HLDS entries (own schema, no Source-only cvars)
idtech.ts Quake 2/3, CoD4, FiveM (set/seta dialect)
arma.ts Arma 2 / 2 OA / 3
family.ts family scaffolding: shared file/format/hint + extras group
fields.ts terse schema field constructors (n/b/t/raw/sel, plus
section() for INI and path() for dotted JSON/YAML keys)
schemas/ curated per-game field schemas
composables/
useConfigForm.ts ConfigDoc + Schema -> grouped fields & writable models
useAsyncPanel.ts load/save state, stale-response guard, panel error text
components/
ConfigEditor.vue generic, format+schema-driven editor
GameConfigTab.vue one tab that switches on server.game_id
LaunchSettingsTab.vue start-command vars via the panel settings API
FieldInput.vue one control per field type, built from the panel's inputs
*.test.ts mounted-component tests (jsdom + @vue/test-utils)
lib/notify.ts the panel's toasts and confirm dialogs, with fallbacks
icons.ts GIcon names: the panel's own plus the ones the plugin registers
styles.css plain CSS, `gce-` prefixed, on the panel's --gameap-* tokens
test/ stand-ins for the panel's components and naive-ui in tests
index.ts plugin definition: 2 tabs + N game-gated file editors
A format turns file text into a ConfigDoc that applies edits in place and
re-serialises, preserving comments, ordering, and every untouched key. A
codec handles that format's value spellings (booleans are True/False in
Palworld/INI, true/false in Minecraft, 1/0 in Source convars; strings
are quoted or not per format). The editor is entirely generic: it drives a
ConfigDoc through its codec, guided by a per-game schema (labelled groups),
and renders anything not in the schema generically so nothing is ever hidden.
-
"Game Config" server tab. GameAP can't gate a tab per game (its slot API has no game filter and a static label), so there's one tab on every server that switches on
server.game_id: it loads the right config file directly via the panel file API (stream-fileto read,update-fileto save), or shows a short "not supported yet" note for games we don't cover. Games with several config files (ARK) get a file selector.This limitation is on its way out. GameAP 4.4.0 adds
checkGame(aGameCheckof engines and/or game codes) to slot components, so the tab could be gated declaratively and the "not supported yet" branch dropped. Blocked for now on the npm side: the typed SDK carrying it is0.3.3, which the 4.4.0 tree declares but npm has never published - the newest published release is0.3.2. Revisit when it lands, rather than hand-rolling the type. -
File-manager editors. These can be game-gated declaratively (
match.gameCode=game_id), so browsing to a matching file offers the structured editor. One is registered per config file, generated from the registry. -
"Launch Settings" server tab. Edits a server's start-command variables through GameAP's settings API (
GET/PUT /api/servers/{id}/settings) instead of a file - the only editor for games whose config is launch args (Valheim). The settings list is self-describing, so the form adapts to whatever the game mod declares (no per-game schema). Writes need the non-admingame-server-settingsability; without it the form is read-only, and it degrades gracefully when a game declares no vars.
- Per-game labelled schemas + a dynamic section/"Advanced" catch-all for unknown keys, and a raw-text fallback if a file doesn't parse.
- Relay guardrail (Palworld): warns when
PublicIPis set and offers a one-click clear - don't leak your home IP behind a WireGuard relay. - Running-server warning: detects
process_activeand reminds you to stop the server before saving (games that rewrite config on shutdown) or restart it for changes to take effect. - Case-insensitive keys for ARK/Unreal INI, so editing a game-written
AllowThirdPersonPlayernever appends a duplicateallowThirdPersonPlayer. - Info notes (e.g. CS2's config-layering caveat) shown inline.
- Pick or write a
Formatinsrc/formats/(most games reuse an existing one). - Author a schema in
src/games/schemas/usingn/b/t/sel(fields.ts),section('Name').n(...)when the format has sections (INI), orpath('a.b').n(...)when it nests (JSON, YAML). - Add a
GameConfigentry tosrc/games/registry.ts(gameId,fileName,dir,format,schema). Both the tab and a game-gated file editor wire up automatically.
If the game belongs to an engine family that is already covered (Source,
GoldSource, idTech/set-dialect, Arma), add a row to that family's defs table
instead of the main registry - family() fills in the file name, format and
load hint every member shares, and withExtras() appends the game's own group
to the family schema. Keep the families' schemas separate even when they look
alike: GoldSource deliberately omits Source-only convars, and merging the two
would start offering settings HLDS ignores.
Requires only Docker and git on the host - TinyGo and Node run in containers.
./build.sh # build the plugin
./build.sh clean # drop build artifacts, keep the SDK checkout
./build.sh distclean # also drop the SDK checkout and cachesThis will:
- check out the GameAP SDK into
./.sdk/gameapat the ref matching your panel (SDK_REF, a tag or branch, defaultv4.4.1;SDK_TAGstill works, andSDK_URLoverrides where it is cloned from). An existing checkout is reset and moved to that ref rather than left as-is; - build the frontend bundle (Vite, via
npm ci) ->frontend/dist/plugin.js+plugin.css; - compile everything to
GameAP-GameConfigEditor.wasmwith TinyGo.
For frontend-only iteration you can cd frontend && npm install && npm run build
with a local Node (no Docker needed for the JS bundle).
cd frontend
npm test # vitest: format round-trips, form building, registry, tabs
npm run typecheck # vue-tsc over src/, plus tsc over the build configThe format layer is where a bug would silently corrupt someone's live server
config, so the tests concentrate there: every format must round-trip an untouched
file byte-for-byte, and editing one key must rewrite exactly that key's line.
npm run test:watch reruns on change.
The component tests mount the tabs and the editor in jsdom, where the panel's
globally registered components (GButton, GIcon, GInput, ...) and its
naive-ui do not exist, so src/test/ supplies stand-ins: panel.ts registers
doubles for the components and for the $message/$dialog globals (a test
steers the next confirm dialog through panel.answer), and vitest.config.ts
aliases naive-ui to naive-ui.ts, plain form elements that emit the same
update:value events. src/icons.test.ts checks that every icon a schema names
is either a panel icon or one the plugin registers. npm run build itself fails
when the bundle still imports an externalized package, is not wrapped in an
IIFE, or a stylesheet selector leaves the gce- namespace
(assertBundleShapePlugin in vite.config.ts).
.forgejo/workflows/frontend.yml runs those two commands plus a bundle build on
every push, and the full ./build.sh on anything that isn't a pull request.
Requires GameAP 4.4.0 or newer: the UI is built from the panel's own components and its naive-ui, which the panel exposes to plugins from that release on.
In the panel: Administration -> Plugins -> Upload, select
GameAP-GameConfigEditor.wasm. Open a server's Game Config tab, or browse to a
supported config file in the file manager.
Upgrading from an earlier build: the plugin id has changed twice (
palworld-settings->game-config-editor-> the marketplace idmfvdrt4f4zlqa), and GameAP treats each id as a separate plugin. Upload the new.wasm, then remove any older copy still listed.
Why the build is shaped the way it is. All of it is handled already - these are written down so a later change doesn't quietly undo one.
- Go 1.26 toolchain. The SDK declares
go 1.26, so the TinyGo image has to be new enough to compile it -build.shpinstinygo/tinygo:0.41.1. An older image capped at Go 1.25 fails at the compile step. - gRPC stubs trimmed.
pkg/protoships host-side*_grpc.pb.gowhose TLS code TinyGo can't compile, sobuild.shdeletes them. The guest never uses them. - SDK vendored via
replace.github.com/gameap/gameapis v4.x with no/v4module path, so it can't bego get-ed - it's cloned to./.sdk/gameap. - CSS must be
plugin.css. Vite names a library stylesheet after the package, butmain.goembedsdist/plugin.css. Fixed bybuild.lib.cssFileName, which also keeps the name stable if the package is ever renamed. - No CSS framework. The styles are one plain file,
src/styles.css, that Vite emits asplugin.css. GameAP injects plugin CSS panel-wide, so every class is prefixedgce-and colours come from the panel's--gameap-*tokens (the panel flips them onhtml.dark, so there are no dark variants to keep in step). Templates use the panel's own components (GButton,GIcon,n-alert,n-form-item, ...) and only the utility classes the panel's compiled CSS contains - its Tailwind build does not scan plugin sources, so a utility the panel doesn't use itself compiles to nothing here; anything responsive lives instyles.css. The plugin used to ship a Tailwind build of its own for this, which duplicated the panel's utilities and needed arevert-layerhack to beat the panel's button reset. Don't reintroduce one. go mod tidywrites to a throwaway modfile. Left alone it rewrites the committedgo.modon every build (bumping thegodirective and the indirect versions), sobuild.shcopies it to.build.mod, pointsGOFLAGS=-modfile=at the copy and removes it on exit. The flip side: the Go dependency versions are resolved per build, so only the npm half of the toolchain is lockfile-pinned.- Version lives in one file.
VERSIONat the repo root is the only declaration:main.goembeds it, andvite.config.tsreads it to inject__PLUGIN_VERSION__into the bundle. Bumping is editing that file, and drift between the Go shell and the frontend is no longer possible. (package.jsonstill carries a version for npm's benefit, but nothing reads it.) - The version must move when the artifact does. Matching version files never
caught the case that matters - two different
.wasmfiles both shipped as 2026.8.3 because the number simply had not been bumped.build.shwarns whenVERSIONstill matches the last tag but a build input has changed since it, which is exactly that situation. "Build input" is wider than the source:build.shitself pins the Node and TinyGo images andvite.config.tsdecides the bundle's shape, so both count. Bump whenever the bundle changes, refactors included; toolchain swaps that produce a byte-identical bundle do not need one. That warning is local-only - a CI checkout is shallow and has no tags to compare against - so the release job separately fails when a pushed tag andVERSIONdisagree, which is the case that would actually ship a mislabelled binary. - Installs are pinned.
package-lock.jsonis committed andbuild.shrunsnpm ci, so a commit always builds against the same versions. Sincefrontend/node_modulesis bind-mounted, a build also resets your local install to match the lockfile. @gameap/debugaccounts for most ofnode_modules. It depends on@gameap/frontend, so installing it brings a second copy of the whole panel - naive-ui, date-fns, highlight.js, lodash, and its own vite 7 alongside our vite 8. That is 191 of the lockfile's 518 entries and ~150 MB, none of it reachable from the bundle. It is kept anyway: it is the only way to run the plugin against a mock API locally, and keeping it in the lockfile meansnpm run debugis pinned and works offline. Mind that the published 0.3.10 bundles a panel build from before 4.4.0 - nowindow.NaiveUI, no theme tokens - so it loads the plugin but renders its tabs empty. To see the UI, run the harness from a panel checkout instead:PLUGINS_PATH=<absolute path to frontend/dist> npm run devingameap-api/web/frontend/packages/gameap-debugserves the real panel frontend with mocked APIs at http://localhost:5174. Nothing inbuild,test, ortypechecktouches it, so if an install ever needs to be lean,npm ci --omit=devor dropping this one entry is the lever -npm run debugthen still works vianpx, just unpinned and needing network.vue,axiosandnaive-uiare peer + dev dependencies, never runtime ones. All three are externalized towindow.Vue/window.axios/window.NaiveUI(seeglobalExternalsPlugininvite.config.ts) because GameAP provides them - naive-ui from 4.4.0 on, which is what sets the minimum panel version - so they must not be bundled;vue-tscand Vitest still need to resolve them from disk, hence the devDependency.@gameap/uiis externalized the same way although nothing here imports it: the plugin SDK re-exports it from 0.3.3 on, and a future bump must not pull the panel's UI package into the bundle. The rewrite reads named imports through optional chaining (window.NaiveUI?.NAlert) and drops bare side-effect imports, because the panel serves every plugin concatenated in one/plugins.jsmodule: a global missing on an older panel has to leave undefined components rather than throw and take the other plugins down with this one.axiosis declared explicitly even though@gameap/debughappens to hoist a copy: relying on a transitive dep's hoisting to satisfy a direct import breaks the moment that dep moves or is dropped.- Rollup types come from
vite, notrollup. We build with vite 8, which bundles rolldown, so nothing here declaresrollup. A copy is still on disk, but only as a transitive of@gameap/debug's vite 7 - a dev harness the build never touches.vite.config.tstherefore names plugin types via vite's re-exportedRollupcompat namespace: importing fromrollupdirectly would lean on that hoisting and break the moment the harness is dropped or bumped, which is the same trapaxioswas already in. - TypeScript is held at 6.x, pinned exactly (
"typescript": "6.0.3", no caret). TS 7 is the native compiler and no longer exportstypescript/lib/tsc, whichvue-tscrequires, sonpm run typecheckdies withERR_PACKAGE_PATH_NOT_EXPORTED. 6.0.3 is the last JS-based release. Apretypecheckstep refuses to run on TS 7 and says why, so anyone who installs it over the pin gets that sentence instead of a Node stack trace. Nothing is lost by staying:vue-tscis the only thing here that invokes TypeScript at all - Vite and Vitest transpile with esbuild - so TS 7's speedup would not touch this build. Revisit whenvue-tscstops requiringtypescript/lib/tsc, which needs thetypescript/unstable/*API to settle.
- Config paths are disk-root-relative (the
serverdisk = the server's install dir). The file-manager editor works regardless of path; only the tab's direct-load uses the registry path. - ARK is registered with both platform folders. Survival Evolved writes
.../LinuxServer/...; Survival Ascended has no native Linux build, runs under Proton and writes.../WindowsServer/...even on a Linux node. Both share thearkgame id, so the entry cannot know which applies - it lists the native one asdirand the Proton one inaltDirs, and the tab probes in order. - Project Zomboid writes
Zomboid/under the process$HOME, which may be outside the server dir. If the tab can't find the file, browse to it in the file manager. The filename also tracks the configured server name. - CS2 keeps
server.cfgatgame/csgo/cfg/(extragame/layer) and layers gameplay convars viagamemode_*_server.cfg- see the inline note.