Conditional Config Sync is a shared infrastructure library for Valheim mods. It does not add gameplay content, items, UI, or configuration options of its own. Install it when another mod lists it as a dependency.
The package provides centralized config synchronization, version checks, server-side policy overrides, protected locking, and synchronized runtime values. Keeping this logic in one standalone dependency means fixes can be shipped by updating this package instead of rebuilding every mod that uses it.
It is an independent synchronization option for mod authors who need per-setting ownership policy, hidden-setting controls, and expanded runtime-value behavior. Jotunn and ServerSync remain separate libraries with their own use cases and development paths.
Normally, a mod manager installs Conditional Config Sync automatically as a dependency. Keep it installed while any enabled mod depends on it.
This package:
- does not replace, modify, or intercept
ServerSyncused by other mods; - does not migrate or modify configuration belonging to other mods;
- can run alongside Jotunn and mods that use
ServerSync; - only handles mods that were explicitly built against Conditional Config Sync.
Installing this package alone has no gameplay effect.
The Thunderstore package contains:
ConditionalConfigSync.Plugin.dll- the BepInEx bootstrap registered as_shudnal.ConditionalConfigSync;ConditionalConfigSync.dll- the public API and synchronization runtime used by dependent mods;ConditionalConfigSync.xml- IntelliSense documentation for IDEs and mod authors;SHA256SUMS.txt- SHA-256 hashes of both release DLL files.
Both DLL files must remain installed together. The XML and documentation files are optional at runtime but useful for development, verification, and redistribution.
Conditional Config Sync uses its own BepInEx and Harmony identifier:
_shudnal.ConditionalConfigSync
It does not patch, replace, or disable Jotunn synchronization or ServerSync. These libraries can coexist in the same process because each mod continues using the synchronization library it was compiled against.
Embedding or ILRepacking ConditionalConfigSync.dll into another mod is intentionally unsupported. An embedded copy rejects initialization and reports an explicit error. Use the standalone Thunderstore dependency instead.
Every registered setting has one of three ownership modes selected by its mod author:
AlwaysServerControlled- always synchronized from the server and cannot be released by policy. Mod authors should use it for shared mechanics and synchronized state that must use the same effective value on the server and every client. Typical examples are gameplay rules, world-state generation, shared events, network-visible calculations, and feature switches that would malfunction when peers disagree;Conditional- uses the mod-defined server/client default and may be overridden by server policy. It is appropriate when an administrator may reasonably choose between one shared server value and per-client behavior;AlwaysClientControlled- always local to each client and cannot be forced by sync policy. It is intended for presentation, local UI, controls, and other behavior that does not participate in shared mechanics.
A mod may also register one locking setting that controls whether ordinary clients may publish changes to server-controlled settings. Hidden-state policy is independent and may hide settings from compatible configuration managers in any ownership mode.
Policy files are created in BepInEx/config/shudnal.ConditionalConfigSync on the server. Existing files are read synchronously during server startup before the file watchers are used. Reads are retried until the file metadata is stable, so common editor save patterns such as truncate/write/rename do not replace a working policy with a partial snapshot. The files are then watched for changes and can be edited while the server is running.
Conditional Config Sync provides a locking model familiar to ServerSync users and adds explicit protection for the locking setting itself.
When the lock is enabled:
- ordinary clients cannot publish changes to server-controlled settings;
- server administrators receive an exemption and may edit them;
- client-controlled settings remain local and editable.
When the lock is disabled, ordinary clients may publish changes to server-controlled settings if the mod author enabled unlocked-client updates.
The locking setting is always protected:
- it is always server-controlled;
- it cannot be forced to client-controlled through policy;
- a non-admin client cannot change it even while the rest of the configuration is unlocked.
This prevents a client from attempting to grant itself permission by changing the lock setting first.
File:
BepInEx/config/shudnal.ConditionalConfigSync/ConditionalConfigSync.SyncPolicy.cfg
A rule may target one exact setting or an entire config section:
ModGuid.Section.Key
ModGuid.Section
Use + to force server ownership and - to force client ownership:
# Exact setting
+ author.mod.General.Damage multiplier
- author.mod.Interface.Show status panel
# Every Conditional setting in a section
+ author.mod.Gameplay
- author.mod.InterfaceRules:
+ identifierforces matchingConditionalsettings to be server-controlled;- identifierforces matchingConditionalsettings to be client-controlled;- an exact setting rule takes precedence over a whole-section rule;
- no matching entry preserves the mod author's default;
AlwaysServerControlledandAlwaysClientControlledignore sync-policy ownership overrides;- malformed, duplicate, conflicting, unknown, and ignored entries can be reported by
conditionalconfigsync_policy_validate; - forcing the protected locking setting to client-controlled is ignored.
Policy is edited only through these files. Console commands intentionally provide reload, validation, status, and dump operations but do not add, remove, or rewrite rules. File editing keeps the complete policy visible, reviewable, and easy to revert.
Typical uses:
- enforce an otherwise client-local gameplay option on a public server;
- release a cosmetic or UI setting from server control;
- switch a complete section without listing every setting;
- override one setting differently from the rest of its section;
- temporarily test a different ownership model without rebuilding the mod.
Hidden settings
File:
BepInEx/config/shudnal.ConditionalConfigSync/ConditionalConfigSync.HiddenConfigs.cfg
Add either an exact setting identifier or a whole-section identifier per line:
# Exact settings
author.mod.Advanced.Internal multiplier
author.mod.Debug.Enable verbose output
# Every setting in a section
author.mod.AdvancedAn exact or section match marks the setting as not browsable in compatible configuration managers. Hiding is a presentation policy, not a security boundary: the setting still exists, and its synchronization behavior is controlled separately by SyncPolicy.cfg, its ConfigSyncMode, and the lock state.
Typical uses:
- hide advanced or dangerous options from ordinary configuration UI;
- simplify a public server's visible settings;
- hide a whole internal section with one line;
- keep diagnostic options available in the config file without advertising them in the manager.
Local debug settings are stored in:
BepInEx/config/shudnal.ConditionalConfigSync/ConditionalConfigSync.Debug.cfg
Console commands for diagnostics:
conditionalconfigsync_debug
conditionalconfigsync_debug_server
Server policy commands:
conditionalconfigsync_status
conditionalconfigsync_policy_reload
conditionalconfigsync_policy_validate
conditionalconfigsync_policy_dump
statusshows registered mods, mode counts, effective server-controlled settings, hidden settings, and policy-file paths;policy_reloadreads and applies both policy files immediately on the Unity main thread;policy_validatereports syntax errors, duplicates, conflicts, unknown identifiers, and rules ignored by fixed modes;policy_dumpwritesConditionalConfigSync.PolicyDump.txtwith copy-ready exact and section identifiers. Each setting is followed only by its policy mode and default ownership; the variable type is intentionally omitted.
Debug logging supports Basic, Verbose, and Trace levels and an optional mod-name filter. Normal startup stays quiet; warnings and errors remain visible without debug logging.
Conditional Config Sync is a third-party standalone dependency. Do not embed it with ILRepack and do not ship private copies inside individual mods.
Reference only:
ConditionalConfigSync.dll
Then declare the standalone BepInEx plugin as a hard dependency:
[BepInDependency(
"_shudnal.ConditionalConfigSync",
BepInDependency.DependencyFlags.HardDependency)]Use the namespace:
using ConditionalConfigSync;The public API keeps familiar ConfigSync names also used by ServerSync:
internal static readonly ConfigSync configSync = new ConfigSync(pluginID)
{
DisplayName = pluginName,
CurrentVersion = pluginVersion,
MinimumRequiredVersion = pluginVersion,
ModRequired = true
};ModRequired controls whether the owning mod must also be installed and compatible on the remote peer. This is separate from the BepInEx hard dependency on Conditional Config Sync itself:
- the hard dependency requires CCS on the same machine where the owning mod is installed;
ModRequired = truerequires a compatible copy of the owning mod on the other side of the connection.
Set it before a connection is established, preferably in the ConfigSync object initializer:
internal static readonly ConfigSync configSync = new ConfigSync(pluginID)
{
DisplayName = pluginName,
CurrentVersion = pluginVersion,
MinimumRequiredVersion = pluginVersion,
ModRequired = true
};The behavior is symmetric:
| Local side running the mod | ModRequired |
Remote side without the mod | Result |
|---|---|---|---|
| Client | true |
Server | Connection is rejected on the client |
| Server | true |
Client | The server rejects that client |
| Client or server | false |
Remote peer | Connection is allowed and this mod instance is not synchronized with the missing remote copy |
Use true for server-authoritative, world-state, gameplay, or other two-sided mods. A mod such as Seasons, which synchronizes the current season, day, settings, and runtime state, must require its remote copy. Leave the default false only for a genuinely client-only mod or an optional integration that remains correct when the other side does not have it.
When the remote copy exists, CurrentVersion, MinimumRequiredVersion, and the CCS wire protocol are used for compatibility checks. Late registration or RequestFullSync() does not repeat connection admission, so configure ModRequired while creating the ConfigSync instance.
Register an existing BepInEx entry with its mode in one call:
ConfigEntry<float> damage = Config.Bind(
"Gameplay",
"Damage multiplier",
1f,
"Server gameplay setting");
configSync.AddConfigEntry(
damage,
ConfigSyncMode.AlwaysServerControlled);Use AlwaysServerControlled whenever the setting controls a common mechanic whose result must remain consistent across the server and all clients. Do not leave such a setting Conditional merely to make it configurable through policy: allowing one client to restore a local value can make shared calculations, events, state transitions, or network-visible behavior diverge.
Or bind and register in one call:
SyncedConfigEntry<bool> showPanel = configSync.AddConfigEntry(
Config,
"Interface",
"Show status panel",
true,
new ConfigDescription("Local UI setting"),
ConfigSyncMode.AlwaysClientControlled);For policy-controlled settings, specify the default ownership without assigning a second property after registration:
SyncedConfigEntry<float> scale = configSync.AddConfigEntry(
Config,
"Gameplay",
"World scale",
1f,
new ConfigDescription("May be reassigned through SyncPolicy"),
ConfigSyncMode.Conditional,
serverControlledByDefault: true);Compatibility overloads remain available:
configSync.AddConfigEntry(entry); // Conditional, server-controlled by default
configSync.AddConfigEntry(entry, false); // Conditional, client-controlled by defaultPassing the mode/default directly prevents a temporary server-controlled registration state and avoids duplicate policy-state transitions during initialization.
Runtime values are also available:
public static readonly CustomSyncedValue<string> mapData =
new(configSync, "Map data", "");Use CustomSyncedValue<T> for state. Equal assignments are suppressed by its comparer. Use SequencedCustomSyncedValue<T> for event-like data where repeated equal payloads must still be delivered in order.
The XML file shipped beside the DLL documents the public API, assignment modes, comparers, priorities, version checks, and integration details directly in IDE IntelliSense.
Conditional Config Sync uses a numeric wire protocol version that is independent from the DLL and package version.
The first public release uses protocol 1. Clients and servers must use the same protocol version. The protocol number is increased only when an incompatible network-format change is introduced; ordinary library updates do not require a protocol bump while the wire contract remains compatible.
Configs and custom values registered after the network session starts are handled automatically:
- the server batches newly registered values and broadcasts their current state;
- a client that registers values after
InitialSyncCompletedrequests a complete resync from the server; - multiple registrations in the same frame are coalesced into one operation.
A ConfigSync instance created after the normal connection startup window also registers its RPC handlers and requests its first complete package automatically. A mod that rebuilds a dynamic registration set can call configSync.RequestFullSync() explicitly. The method returns false when no remote server is connected.
Connection admission and mod-version validation still happen during Valheim's normal peer handshake. Mods that require version enforcement should create their ConfigSync instance before connecting; late resync updates data but does not retroactively repeat the completed connection check.
Each ConfigSync instance exposes optional events for mods that need to rebuild derived runtime state or provide their own diagnostics:
InitialSyncCompletedafter the first complete server package has been applied on a client;ServerConnectionResetafter server values are cleared and local fallback values are restored;PolicyStateChanged,ServerControlledChanged, andHiddenStateChangedwhen effective policy changes;LockStateChangedwhen the effective lock or administrator exemption changes;SyncRejectedwhen a package or outgoing update is rejected for permissions, malformed data, queue overflow, or the 20 MiB safety limit.
Subscriber exceptions are isolated and logged, so one consumer cannot interrupt synchronization for the remaining mods or entries. Full usage notes are included in the XML documentation.
Compared with embedding a sync source file or ILRepacking a private DLL:
- all dependent mods use one maintained implementation;
- fixes to networking, policy, serialization, or security require updating only this package;
- duplicate Harmony patches, watchers, commands, and static registries are avoided;
- server administrators get one common policy layer for all participating mods;
- dependent mod packages remain smaller and simpler.
For Thunderstore, add this package to the mod's dependencies. Do not copy either Conditional Config Sync DLL into the dependent mod's own package.
- Delivered as a standalone BepInEx hard dependency instead of being embedded with ILRepack.
- Uses a dedicated namespace, BepInEx GUID, Harmony ID, RPC names, and package format.
- Does not modify or intercept ServerSync instances used by other mods.
- Detects and rejects unsupported embedded Conditional Config Sync copies.
- Retains separate local and active server values on clients.
- Supports runtime switching between server-controlled and client-controlled state.
- Adds three explicit config modes:
AlwaysServerControlled,Conditional, andAlwaysClientControlled. - Adds server-side
SyncPolicy.cfgoverrides for exact settings and complete sections. - Adds exact-setting and section-level
HiddenConfigs.cfgrules for Configuration Manager visibility. - Sends effective config state together with server values.
- Keeps the lock and administrator exemption model familiar from ServerSync.
- Protects the locking setting itself even while the configuration is unlocked.
- Rejects non-admin network attempts to change the protected lock.
- Uses the connected peer identity for current Valheim admin checks and clearer logs.
- Distinguishes state-like
CustomSyncedValue<T>from event-likeSequencedCustomSyncedValue<T>. - Supports explicit
AssignLocalValue,AssignLocalValueIfChanged, andAssignLocalValueAndNotifybehavior. - Supports custom equality comparers for collections and domain types.
- Supports priorities and preserves ordering for sequenced values.
- Defers outgoing updates safely instead of dropping changes during active synchronization.
- Regular
ConfigEntryvalues use BepInEx TOML conversion based on the local setting type. - Custom values retain typed Valheim package serialization and support
ISerializableParameter. - Uses length-prefixed entries so unknown package entries can be skipped safely.
- Adds compression, fragmentation, queue limits, payload limits, and clearer failure logging.
- Adds an independent numeric protocol version with exact client/server matching.
- Supports automatic late registration and explicit complete resynchronization.
- Adds stable/retried policy reads so partial editor writes do not replace the active policy.
- Cleans up policy watchers, network caches, pending queues, registered session handlers, and Harmony ownership when the relevant session or bootstrap ends.
- Adds per-mod receive logs with config/custom-value names for single-value updates.
- Includes optional filtered debug logging and server policy status/reload/validate/dump commands.
- Ships XML IntelliSense documentation and metadata descriptions for important public API members.
Using Conditional Config Sync in a mod selects its synchronization implementation and protocol for that mod. Server and clients should update the dependent mod and this dependency together. Other installed mods that use ServerSync are unaffected.
Original Conditional Config Sync contributions are released under the Unlicense. The project uses the ServerSync approach and adapted code as a foundation; applicable ServerSync portions remain covered by its MIT-0 terms. Some implementation and compatibility ideas were informed by Jotunn. The retained notices are available in THIRD_PARTY_NOTICES.md.
Conditional Config Sync:
- contains no telemetry and sends no data to external services;
- makes no HTTP requests and opens no network connections outside the active Valheim client/server session;
- does not download, update, or execute external programs;
- does not access the Windows registry or ship native libraries;
- communicates only through Valheim RPC connections between the current server and its connected clients;
- writes only its policy, debug, dump, and diagnostic files under
BepInEx/config/shudnal.ConditionalConfigSync; - uses reflection only to access Valheim runtime members whose accessibility differs from publicized development assemblies;
- publishes unobfuscated binaries, XML API documentation, source code, and SHA-256 hashes for both DLL files.