Skip to content

Releases: Dildz/ModSync-for-SPT4

Corter ModSync v0.13.2 (SPT 4.1.x)

Choose a tag to compare

@github-actions github-actions released this 23 Aug 16:34

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:ro

Nothing 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

Full Changelog: v0.13.0...v0.13.2

Corter ModSync v0.13.1-pre1 (SPT 4.1.x)

Pre-release

Choose a tag to compare

@github-actions github-actions released this 22 Aug 07:14

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 to SPT_Runtime/, but ModSync still told clients that a syncPath like user/mods lived under SPT\. 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: 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 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

Full Changelog: v0.13.0...v0.13.1-pre1

Corter ModSync v0.12.7 (SPT 4.0.13)

Choose a tag to compare

@github-actions github-actions released this 22 Aug 06:55

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: 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 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

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)

Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Aug 10:44

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: 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 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

Full Changelog: v0.12.7-pre1...v0.12.7-pre2

Corter ModSync v0.13.0 (SPT 4.1.x)

Choose a tag to compare

@github-actions github-actions released this 10 Aug 12:45

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: IPreSptLoadModAsync was removed (it's IOnLoad + OnLoadAsync now), IHttpListener changed shape, and log colours moved from LogTextColor to Spectre. No config changes are needed — your config.jsonc carries over as-is.
  • The server mod now installs to SPT_Runtime/user/mods/. SPT 4.1 renamed its server folder from SPT/ to SPT_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. baseFiles assumed 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-bak means 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's nvngx_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 baseFiles backs 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.jsonc setups, from minimal to a real server

Full Changelog: v0.12.6...v0.13.0

v0.12.6

Choose a tag to compare

@github-actions github-actions released this 19 Jul 17:35

Changes

  • New: baseFiles — sync mods that replace base-game files. A syncPath can now list base-game files the mod overwrites (e.g. DLSS's nvngx_dlss.dll, DynamicMaps' Unity assemblies). ModSync backs the original up as .modsync-bak before 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: headless per syncPath — set "headless": false and 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

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

Choose a tag to compare

@github-actions github-actions released this 27 Jun 14:30

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 headless Corter-ModSync-Prepatch.dll patcher, by adding the path to that install's ModSync_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.jsonc now documents the syncPaths object form — each entry can be a plain string or an object with enabled / enforced / silent / restartRequired / name to 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 DirectoryNotFoundException during download or apply. Paths now use the extended-length prefix on Windows; no effect on shorter paths or Linux/headless.
  • Fix: Version.txt now 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 default SPT/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 except config.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

Choose a tag to compare

@github-actions github-actions released this 11 Jun 20:58

Changes

  • Patcher: per-file update apply — each staged file is applied and cleared independently. One failed file no longer keeps the entire PendingUpdates folder (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-old and replaced in place; the leftover .modsync-old files 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 overwrites LogOutput.log on every boot
  • Fix: startup warning pointed to a log file that doesn't exist — it now points to ModSync_Data/ModSync.log on 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

Choose a tag to compare

@github-actions github-actions released this 04 Jun 19:08

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) where Updater.exe cannot 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: headlessIncludes now overrides global exclusions — files in both exclusions and headlessIncludes (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

v0.12.2

Choose a tag to compare

@github-actions github-actions released this 02 Jun 09:49

Full Changelog: v0.12.1...v0.12.2