Repository navigation
Releases: Dildz/ModSync-for-SPT4
Release list
Corter ModSync v0.13.2 (SPT 4.1.x)
Requires SPT 4.1.3 (specifically) - use v0.13.0 if you are still on SPT4.1.2.
Still on SPT 4.0.x? Use v0.12.7. That line is still supported on the SPT4.0.x branch - both versions get the same features.
Changes
This is the first full release since v0.13.0. It carries everything from the v0.13.1-pre1 plus new support for headless clients on SPT 4.1.3.
Headless clients can get their asset bundles on SPT 4.1.3
SPT 4.1.3 moved bundle downloading out of the game client and into the launcher. A headless never runs the launcher, so on 4.1.3 it cannot download bundles.
ModSync now fetches them itself on a headless, before the game loads them. Bundles are CRC-verified and cached, so this happens once.
**If your headless shares a machine with your server, this shouldn't be an issue as SPT still falls back to reading a bundle out of its mod folder when the cache misses. If using docker and headless is in the same stack you can use:
# headless service
volumes:
- ../headless:/opt/tarkov
- ../server/SPT_Runtime/user/mods:/opt/tarkov/SPT_Runtime/user/mods:roNothing is copied and no extra disk is used. ModSync then sees that every bundle is already reachable and downloads nothing - a 7 GB modset goes from a ~50 minute first boot to about two minutes. Without the mount, ModSync downloads them, which works everywhere but is limited to roughly 2-3 MB/s by Unity's Mono HTTP stack. Either way you end up with a working headless.
Note
Bundle fetching is a stopgap. It exists because there is no headless image for SPT 4.1.3 yet.
Once an official one ships and handles bundles itself, this will be removed from ModSync.
The mod-folder mount shown above is not affected and will keep working - that is SPT's own
lookup behaviour, not something ModSync adds.
Also, its slow without the mount, painfully so, but it works. If you have a networked setup with gigs of bundles, go have a Fika break!
New: optional per syncPath
Set "optional": true and the client gets a working checkbox for that path in the F12 menu, starting in whatever state enabled gives it. Until now the only path a player could tick or untick was an opt-IN one ("enabled": false); this adds the other half, a mod that is synced by default but can still be refused. Unticking uninstalls what ModSync installed there.
Do not put it on the three catch-alls (../BepInEx/plugins, patchers, config) - they carry the mods your players need to connect, so one untick would strip the modset.
New: removing a mod from your config now removes it from clients
Delete a mod's files from the server and delete its syncPaths entry, and every client uninstalls its copy on the next sync, with the usual prompt. Previously the entry disappearing meant clients never heard about it again and kept the mod permanently.
ModSync records what it served in .modsync-served.json next to config.jsonc, and keeps offering a removed entry as empty until you re-add it or clear that file. Deleting the entry but keeping the files still does the opposite, by design: the entry is what carves a mod out of the catch-alls, so without it they sync it to everyone.
Fixed: renaming a syncPath no longer resets your players' F12 choices
Each client stored its toggles under the syncPath's name, so relabelling an entry made every player's setting for that mod revert to default and left a dead line in their config. Toggles are now stored under the path and merely displayed using the name. Each client carries its existing choices over on the first launch and tidies the old lines away; nobody has to do anything.
This mattered more once optional existed: a reset re-ticks an opt-out path, which would have silently reinstalled a mod the player had refused.
Upgrade notes
Unzip into your game/server root as usual - clients update themselves via sync, no manual steps.
Your existing config.jsonc is untouched and behaves exactly as before; optional defaults to false.
If you already have an entry you expected players to be able to untick, it needs "optional": true adding. An "enabled": true path has never been shown in the F12 menu, and still isn't without that option.
Testing status
The four items carried over from v0.13.1-pre1 were tested on the 4.0 line through the v0.12.7 pre-releases, on live servers with headless and player clients, before being ported here unchanged.
Bundle acquisition is new in this release. It was tested on a live SPT 4.1.3 stack with a 1214-bundle / 7 GB modset, both ways: with the mount present (every bundle resolved from the mod folder, nothing downloaded) and with bundles deliberately hidden from it (fetched, CRC-verified and cached). Both test suites are green in CI.
Docs
- syncPaths options - all eight, including
optional - Retiring a mod - how removal reaches clients, and the keep-the-files trap
- Mods that replace base-game files - how
baseFilesbacks up and restores originals - Usage Examples - worked
config.jsoncsetups, from minimal to a real server
Full Changelog: v0.13.0...v0.13.2
Corter ModSync v0.13.1-pre1 (SPT 4.1.x)
Requires SPT 4.1.x
Built against SPT 4.1.1 and declared as ~4.1.0, so it loads on any 4.1.x - including 4.1.3, which renamed the NuGet packages to SPTushonka.* but changed nothing ModSync uses. It will not load on SPT 4.0.x: the 4.1 server checks which SPTarkov.Server.Core a mod was compiled against and refuses to load it on a mismatch.
Still on SPT 4.0.x? Use v0.12.7. That line is still supported on the SPT4.0.x branch - both versions get the same features.
Changes
This brings the 4.1 line level with v0.12.7 on the 4.0 line, plus one fix that only affects 4.1.
- Fixed: paths inside the server folder were advertised as
SPT\...on 4.1. SPT 4.1 renamed its server folder toSPT_Runtime/, but ModSync still told clients that a syncPath likeuser/modslived underSPT\. Nothing failed loudly - hashes matched and downloads worked - the files simply landed in<gameRoot>/SPT/, a folder 4.1 doesn't have. The folder name is now read from the server itself rather than hardcoded, so it is correct on both lines and survives any future rename. - New:
optionalper syncPath. Set"optional": trueand the client gets a working checkbox for that path in the F12 menu, starting in whatever stateenabledgives it. Until now the only path a player could tick or untick was an opt-IN one ("enabled": false); this adds the other half, a mod that is synced by default but can still be refused. Unticking uninstalls what ModSync installed there.
Do not put it on the three catch-alls (../BepInEx/plugins,patchers,config) - they carry the mods your players need to connect, so one untick would strip the modset. - New: removing a mod from your config now removes it from clients. Delete a mod's files from the server and delete its
syncPathsentry, and every client uninstalls its copy on the next sync, with the usual prompt. Previously the entry disappearing meant clients never heard about it again and kept the mod permanently.
ModSync records what it served in.modsync-served.jsonnext toconfig.jsonc, and keeps offering a removed entry as empty until you re-add it or clear that file. Deleting the entry but keeping the files still does the opposite, by design: the entry is what carves a mod out of the catch-alls, so without it they sync it to everyone. - Fixed: renaming a syncPath no longer resets your players' F12 choices. Each client stored its toggles under the syncPath's
name, so relabelling an entry made every player's setting for that mod revert to default and left a dead line in their config. Toggles are now stored under thepathand merely displayed using the name. Each client carries its existing choices over on the first launch and tidies the old lines away; nobody has to do anything.
This mattered more onceoptionalexisted: a reset re-ticks an opt-out path, which would have silently reinstalled a mod the player had refused.
Upgrade notes
Unzip into your game/server root as usual - clients update themselves via sync, no manual steps.
Your existing config.jsonc is untouched and behaves exactly as before; optional defaults to false.
If you had a syncPath without a ../ prefix (user/mods and the like), your players have a stray <gameRoot>/SPT/ folder holding the old copy. ModSync now serves that path to the correct place and stops tracking the old one, so the leftovers are inert but will not be cleaned up automatically - delete that SPT/ folder on each client once everyone has updated.
If you already have an entry you expected players to be able to untick, it needs "optional": true adding. An "enabled": true path has never been shown in the F12 menu, and still isn't without that option.
Testing status
The three features above were tested on the 4.0 line through the v0.12.7 pre-releases, on live servers with headless and player clients, before being ported here unchanged. Both test suites are green in CI.
The SPT_Runtime path fix is new in this release and specific to 4.1. If you serve a syncPath that lives inside the server folder, that is the part worth watching - please open an issue if anything lands somewhere unexpected.
Docs
- syncPaths options - all eight, including
optional - Retiring a mod - how removal reaches clients, and the keep-the-files trap
- Mods that replace base-game files - how
baseFilesbacks up and restores originals - Usage Examples - worked
config.jsoncsetups, from minimal to a real server
Full Changelog: v0.13.0...v0.13.1-pre1
Corter ModSync v0.12.7 (SPT 4.0.13)
Requires SPT 4.0.13
Built against SPT 4.0.13 and declared as ~4.0.0, so it loads on any 4.0.x. It will not load on SPT 4.1.x: the server checks which SPTarkov.Server.Core a mod was compiled against and refuses to load it on a mismatch.
On SPT 4.1.x? Use v0.13.0 instead. Both lines are maintained.
Three changes, tested on live servers through the v0.12.7-pre builds. The two config features do nothing until you edit config.jsonc; the third is a one-time tidy-up each client does to its own settings file.
Changes
- New:
optionalper syncPath. Set"optional": trueand the client gets a working checkbox for that path in the F12 menu, starting in whatever stateenabledgives it. Until now the only path a player could tick or untick was an opt-IN one ("enabled": false); this adds the other half, a mod that is synced by default but can still be refused. Unticking uninstalls what ModSync installed there.
Do not put it on the three catch-alls (../BepInEx/plugins,patchers,config) - they carry the mods your players need to connect, so one untick would strip the modset. - New: removing a mod from your config now removes it from clients. Delete a mod's files from the server and delete its
syncPathsentry, and every client uninstalls its copy on the next sync, with the usual prompt. Previously the entry disappearing meant clients never heard about it again and kept the mod permanently.
ModSync records what it served in.modsync-served.jsonnext toconfig.jsonc, and keeps offering a removed entry as empty until you re-add it or clear that file. Deleting the entry but keeping the files still does the opposite, by design: the entry is what carves a mod out of the catch-alls, so without it they sync it to everyone. - Fixed: renaming a syncPath no longer resets your players' F12 choices. Each client stored its toggles under the syncPath's
name, so relabelling an entry made every player's setting for that mod revert to default and left a dead line in their config. Toggles are now stored under thepathand merely displayed using the name. Each client carries its existing choices over on the first launch and tidies the old lines away; nobody has to do anything.
This mattered more onceoptionalexisted: a reset re-ticks an opt-out path, which would have silently reinstalled a mod the player had refused.
Upgrade notes
Unzip into your game/server root as usual - clients update via sync, no manual steps.
Your existing config.jsonc is untouched and behaves exactly as before; optional defaults to false.
If you already have an entry you expected players to be able to untick, it needs "optional": true adding. An "enabled": true path has never been shown in the F12 menu, and still isn't without that option.
Docs
- syncPaths options - all eight, including
optional - Retiring a mod - how removal reaches clients, and the keep-the-files trap
- Usage Examples - worked
config.jsoncsetups, from minimal to a real server
Coming from before v0.12.4? See the v0.12.4 notes for the one-time BepInEx/patchers/Corter-ModSync/ folder cleanup on the server/headless.
Full Changelog: v0.12.6...v0.12.7
Corter ModSync v0.12.7-pre2 (SPT 4.0.x)
Requires SPT 4.0.x
Built against SPT 4.0.13 and declared as ~4.0.0, so it loads on any 4.0.x. It will not load on SPT 4.1.x: the server checks which SPTarkov.Server.Core a mod was compiled against and refuses to load it on a mismatch.
On SPT 4.1.x? Use v0.13.0 instead. Both lines are maintained.
Pre-release. Three changes worth testing before this goes out as v0.12.7. The two config features do nothing until you edit config.jsonc; the third is a one-time tidy-up each client does to its own settings file.
Changes
- New:
optionalper syncPath. Set"optional": trueand the client gets a working checkbox for that path in the F12 menu, starting in whatever stateenabledgives it. Until now the only path a player could tick or untick was an opt-IN one ("enabled": false); this adds the other half, a mod that is synced by default but can still be refused. Unticking uninstalls what ModSync installed there.
Do not put it on the three catch-alls (../BepInEx/plugins,patchers,config) - they carry the mods your players need to connect, so one untick would strip the modset. - New: removing a mod from your config now removes it from clients. Delete a mod's files from the server and delete its
syncPathsentry, and every client uninstalls its copy on the next sync, with the usual prompt. Previously the entry disappearing meant clients never heard about it again and kept the mod permanently.
ModSync records what it served in.modsync-served.jsonnext toconfig.jsonc, and keeps offering a removed entry as empty until you re-add it or clear that file. Deleting the entry but keeping the files still does the opposite, by design: the entry is what carves a mod out of the catch-alls, so without it they sync it to everyone. - Fixed: renaming a syncPath no longer resets your players' F12 choices. Each client stored its toggles under the syncPath's
name, so relabelling an entry made every player's setting for that mod revert to default and left a dead line in their config. Toggles are now stored under thepathand merely displayed using the name. Each client carries its existing choices over on the first launch and tidies the old lines away; nobody has to do anything.
This mattered more onceoptionalexisted: a reset re-ticks an opt-out path, which would have silently reinstalled a mod the player had refused.
Upgrade notes
Unzip into your game root as usual - clients update via sync, no manual steps.
Your existing config.jsonc is untouched and behaves exactly as before; optional defaults to false.
If you already have an entry you expected players to be able to untick, it needs "optional": true adding. An "enabled": true path has never been shown in the F12 menu, and still isn't without that option.
Docs
- syncPaths options - all eight, including
optional - Retiring a mod - how removal reaches clients, and the keep-the-files trap
- Usage Examples - worked
config.jsoncsetups, from minimal to a real server
Full Changelog: v0.12.7-pre1...v0.12.7-pre2
Corter ModSync v0.13.0 (SPT 4.1.x)
Requires SPT 4.1.x
Built against SPT 4.1.1 and declared as ~4.1.0, so it loads on any 4.1.x — verified running on 4.1.2. It will not load on SPT 4.0.x: the 4.1 server checks which SPTarkov.Server.Core a mod was compiled against and refuses to load it on a mismatch.
Still on SPT 4.0.x? Stay on v0.12.6. That line is still supported on the SPT4.0.x branch — both versions get fixes.
Changes
- SPT 4.1 support. The server mod is ported to the 4.1 API:
IPreSptLoadModAsyncwas removed (it'sIOnLoad+OnLoadAsyncnow),IHttpListenerchanged shape, and log colours moved fromLogTextColorto Spectre. No config changes are needed — yourconfig.jsonccarries over as-is. - The server mod now installs to
SPT_Runtime/user/mods/. SPT 4.1 renamed its server folder fromSPT/toSPT_Runtime/, and the zip follows. Extract into your game root exactly as before. Client-side paths (BepInEx/, the Updater at the game root) are unchanged. - Everything targets .NET 10 now, including
ModSync.Updater.exe. SPT 4.1 runs on .NET 10, and .NET doesn't roll forward across major versions — a .NET 9 Updater would simply refuse to start on a clean 4.1 install. - Fix: mods that ADD files outside their own folder are now uninstalled properly.
baseFilesassumed a mod always replaces a base-game file, so it treated "no.modsync-bak" as proof the file belonged to the game and refused to delete it. Mods that add a file instead of replacing one — DynamicMaps ships two Unity assemblies EFT doesn't have — left those files behind when removed, and their F12 entry locked itself so it couldn't be re-ticked either. Removal now keys off the backup alone: a.modsync-bakmeans restore the original, no backup means the mod added the file, so it's removed like anything else. Mods that genuinely replace a file (Tarkov DLSS'snvngx_dlss.dll) are unaffected — they still get a backup and still restore on opt-out.
Upgrade notes
Unzip into your game root as usual — clients update themselves via sync, no manual steps.
Your config.jsonc is untouched and needs no changes for 4.1.
If you ran 0.12.6 with a baseFiles entry for a mod that adds files (DynamicMaps), those files were left behind when the mod was removed. They're inert on their own, and 0.13.0 cleans them up the next time that mod is un-ticked.
Testing status
Run on a live SPT 4.1.2 server with both a Fika headless and a real player client, syncing several mods end to end. Both test suites are green in CI as well.
That includes DynamicMaps, which is the mod the baseFiles fix above was written for: the case where a mod adds files EFT doesn't ship rather than replacing existing ones. Install, sync and removal all behaved as expected.
Not every configuration can be covered by one server, so if you hit something, please open an issue.
Docs
- Mods that replace base-game files — how
baseFilesbacks up and restores originals. Note the wiki documents the 4.0.x line; the removal behaviour described above is 4.1-only for now. - Usage Examples — worked
config.jsoncsetups, from minimal to a real server
Full Changelog: v0.12.6...v0.13.0
v0.12.6
Changes
- New:
baseFiles— sync mods that replace base-game files. A syncPath can now list base-game files the mod overwrites (e.g. DLSS'snvngx_dlss.dll, DynamicMaps' Unity assemblies). ModSync backs the original up as.modsync-bakbefore replacing it and restores it when the mod is removed, so opting out puts the game back exactly as it was. This replaces the hardcoded DynamicMaps handling with a general mechanism any mod can use. - New:
headlessper syncPath — set"headless": falseand that path is never offered to a headless client. Useful for anything graphical (DLSS, map overlays), which a headless renders nothing for and simply wastes disk and bandwidth on. - New: ModSync updates itself first, then restarts. When the client's version doesn't match the server's, the server serves only ModSync's own components until the client is current. This removes a class of failures where an out-of-date client tried to interpret a newer server's response.
- New:
config_default.jsonc— the server writes the current shipped defaults alongside your config on every boot, so you can diff new options in without your own file ever being touched. - New: notification for missing config options — if a release adds a top-level option your config lacks, the server logs it once at startup. It never rewrites your file.
- Rework: the F12 menu. Optional mods get a real checkbox that installs and uninstalls them. Enforced and built-in paths are now shown rather than hidden — as a one-line explanation with no control, so you can see what the server manages without being able to break it. The three catch-all paths are hidden entirely.
- The zip no longer ships
config.jsonc. The server writes it on first boot if missing, so extracting over an existing install can no longer clobber your config.
Upgrade notes
Unzip into your game root as usual — clients update via sync, no manual steps.
Your existing config.jsonc is untouched: baseFiles and headless are optional and default to the previous behaviour. To use them, copy the examples from the regenerated config_default.jsonc.
Upgrading leaves one dead entry, (Builtin) Managed Files, in each client's BepInEx/config/corter.modsync.cfg. It is unbound and does nothing; delete the line if it bothers you.
Docs
- Mods that replace base-game files — how
baseFilesbacks up and restores originals, and why a hand-installed mod gets locked - Usage Examples — worked
config.jsoncsetups, from minimal to a real server
Coming from before v0.12.4? See the v0.12.4 notes for the one-time BepInEx/patchers/Corter-ModSync/ folder cleanup on the server/headless.
Full Changelog: v0.12.5...v0.12.6
v0.12.5
Changes
- New: trim ModSync's own dead-weight components per client — a headless admin can drop the desktop
ModSync.Updater.exe(headless never runs it), and a player can drop the headlessCorter-ModSync-Prepatch.dllpatcher, by adding the path to that install'sModSync_Data/Exclusions.jsonc. Each component stays enforced on the side that does need it, so you can't accidentally break your own updates. Off by default — both still ship everywhere unless you opt out. See the wiki. - The default
config.jsoncnow documents thesyncPathsobject form — each entry can be a plain string or an object withenabled/enforced/silent/restartRequired/nameto control how that path syncs. These options already worked; they're now shown inline with examples (and the file links the full config guide). Existing configs are unaffected. - Fix: Windows long-path (MAX_PATH) crashes — a deep install combined with a deeply-nested mod could exceed Windows' 260-character path limit and fail with
DirectoryNotFoundExceptionduring download or apply. Paths now use the extended-length prefix on Windows; no effect on shorter paths or Linux/headless. - Fix:
Version.txtnow advances on patch upgrades — clients on the same minor version (e.g. 0.12.0 → 0.12.x) no longer get stuck reporting an out-of-date version on every boot. Existing stuck installs self-heal on the first boot after updating. - Polish: coloured server console logs — the startup banner is green and the routine per-request hash line is greyed, so ModSync is easier to spot in a busy server console.
Upgrade notes
⚠️ Back up your server config before extracting. The zip ships a defaultSPT/user/mods/Corter-ModSync/config.jsonc, so unzipping over an existing server install overwrites your customised config (syncPaths, exclusions, headlessIncludes, etc.). Copy it aside first and merge your settings back, or extract everything exceptconfig.jsonc. Client installs have no server config to lose.
Otherwise unzip into your game root as usual — clients update via sync, no manual steps.
Coming from before v0.12.4? See the v0.12.4 notes for the one-time BepInEx/patchers/Corter-ModSync/ folder cleanup on the server/headless.
Full Changelog: v0.12.4...v0.12.5
v0.12.4
Changes
- Patcher: per-file update apply — each staged file is applied and cleared independently. One failed file no longer keeps the entire
PendingUpdatesfolder (and the "found previous update" warning) stuck on every boot - Patcher: can now update itself — DLLs that are loaded in memory (including the patcher itself and other preloader patchers, which BepInEx always loads before running them) are renamed aside as
*.modsync-oldand replaced in place; the leftover.modsync-oldfiles are cleaned up automatically on the next boot - Patcher: already-current files are skipped — staged files that are byte-identical to what's installed are discarded silently instead of being re-applied every boot
- Patcher: fault-tolerant file removal — one failed deletion no longer aborts the remaining removals; only genuinely failed entries are retried on the next boot
- New: persistent update log on headless — the patcher now logs everything it does to
ModSync_Data/ModSync.log(the same file the Windows Updater writes), so headless setups keep an update history even though BepInEx overwritesLogOutput.logon every boot - Fix: startup warning pointed to a log file that doesn't exist — it now points to
ModSync_Data/ModSync.logon all platforms
Upgrade notes
Unzip into your game root as usual.
Upgrading an existing install: earlier versions shipped the patcher as BepInEx/patchers/Corter-ModSync/Corter-ModSync.Patcher.dll. It is now BepInEx/patchers/Corter-ModSync-Prepatch.dll (no subfolder). On the server — and any manually managed headless — delete the old BepInEx/patchers/Corter-ModSync/ folder after upgrading; if it stays on the server, clients will keep syncing the obsolete patcher back down. Synced clients clean it up automatically when Delete removed files is enabled.
Full Changelog: v0.12.3...v0.12.4
v0.12.3
Changes
- New: BepInEx preloader patcher (
Corter-ModSync-Prepatch.dll) — applies staged mod updates before plugin DLLs are loaded, preventing locked-file errors; critical for headless clients (Docker/Linux) whereUpdater.execannot run - Fix: headless re-sync loop — headless clients now quit cleanly after staging updates instead of attempting an in-process apply
- Fix: preloader patcher properly fires — the patcher was being skipped on every boot before this fix
- Fix:
headlessIncludesnow overrides global exclusions — files in bothexclusionsandheadlessIncludes(e.g.Fika.Headless.dll) now correctly reach headless clients as intended
Install notes
Unzip into your game/server root as usual. Configure sync paths in ../SPT/user/mods/Corter-ModSync/config.jsonc & start the server.
Have connecting players install ModSync (server component not needed for non-host players/headless clients).
ModSync will distribute all mods to headless & player clients according to the scope mapped out in the config.
After the 1st startup/sync, players that would like to exclude a mod from syncing to their machine can edit the Exclusions.jsonc in ModSync_Data. On the next sync, mods listed in Exclusions will be removed by ModSync & will not get synced from the server again.
Full Changelog: v0.12.2...v0.12.3