Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ConditionalConfigSync v1.0.10
ConditionalConfigSync.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Conditional Config Sync")] [assembly: AssemblyDescription("Shared config synchronization and server policy library for Valheim mods.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("shudnal")] [assembly: AssemblyProduct("Conditional Config Sync")] [assembly: AssemblyCopyright("Copyright © shudnal 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("566458c7-717b-4583-af37-a69e4cd02966")] [assembly: AssemblyFileVersion("1.0.10")] [assembly: AssemblyInformationalVersion("1.0.10")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/shudnal/ConditionalConfigSync")] [assembly: AssemblyMetadata("License", "Unlicense")] [assembly: InternalsVisibleTo("ConditionalConfigSync.Plugin")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ConditionalConfigSync { [Description("Registers and synchronizes config entries and runtime values for one mod.")] public class ConditionalConfigSync { internal static class ConfigEntryOnSettingChangedPatch { internal static bool Prefix(ConfigEntryBase __instance) { OwnConfigEntryBase configData = GetConfigData(__instance); if (configData != null) { ConditionalConfigSync owningConfigSync = GetOwningConfigSync(configData); if (owningConfigSync != null && !owningConfigSync.configsBeingApplied.Contains(__instance) && !IsWritableConfig(configData)) { owningConfigSync.RestoreRejectedConfigChange(__instance, configData, owningConfigSync.GetWriteRejectionReason(configData)); return false; } } return true; } } internal static class ConfigEntryGetSerializedValuePatch { internal static bool Prefix(ConfigEntryBase __instance, ref string __result) { OwnConfigEntryBase configData = GetConfigData(__instance); if (configData == null || (IsWritableConfig(configData) && (!configData.HasLocalBaseValue || !ShouldStoreLocalConfigValue(configData)))) { return true; } object obj = (configData.HasLocalBaseValue ? configData.LocalBaseValue : (configData.HasLastAcceptedValue ? configData.LastAcceptedValue : __instance.BoxedValue)); __result = TomlTypeConverter.ConvertToString(obj, __instance.SettingType); return false; } } internal static class ConfigEntrySetSerializedValuePatch { internal static bool Prefix(ConfigEntryBase __instance, string value) { OwnConfigEntryBase configData = GetConfigData(__instance); if (configData == null) { return true; } if (!ShouldStoreLocalConfigValue(configData) && IsWritableConfig(configData)) { return true; } try { configData.StoreLocalBaseValue(TomlTypeConverter.ConvertToValue(value, __instance.SettingType)); } catch (Exception ex) { LogSource.LogWarning((object)$"[ConfigFile] Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}"); } return false; } } [Description("Diagnostic logging verbosity for ConditionalConfigSync.")] public enum ConditionalConfigSyncDebugLevel { Basic = 1, Verbose, Trace } private readonly struct ReceivedConfigState { public readonly bool ServerControlled; public readonly bool Hidden; public ReceivedConfigState(bool serverControlled, bool hidden) { ServerControlled = serverControlled; Hidden = hidden; } } private class ParsedConfigs { public readonly Dictionary<OwnConfigEntryBase, object?> configValues = new Dictionary<OwnConfigEntryBase, object>(); public readonly Dictionary<CustomSyncedValueBase, object?> customValues = new Dictionary<CustomSyncedValueBase, object>(); public readonly Dictionary<OwnConfigEntryBase, ReceivedConfigState> configStates = new Dictionary<OwnConfigEntryBase, ReceivedConfigState>(); public readonly Dictionary<OwnConfigEntryBase, ReceivedConfigState> clientConfigStateClaims = new Dictionary<OwnConfigEntryBase, ReceivedConfigState>(); public readonly List<PolicyStateChangedEventArgs> policyTransitions = new List<PolicyStateChangedEventArgs>(); public int? capabilities; public string? rejectionReason; public int entryCount; public void Reject(string reason) { if (rejectionReason == null) { rejectionReason = reason; } } } private enum PackageEntryKind : byte { Config = 1, CustomValue, ServerVersion, LockExempt, ConfigState } private class PackageEntry { public PackageEntryKind kind; public string? section; public string? key; public Type? type; public object? value; public bool serverControlled; public bool hidden; public int? capabilities; public static PackageEntry ServerVersion(string version) { return new PackageEntry { kind = PackageEntryKind.ServerVersion, value = version }; } public static PackageEntry LockExempt(bool value, int? capabilities = null) { return new PackageEntry { kind = PackageEntryKind.LockExempt, value = value, capabilities = capabilities }; } public static PackageEntry ConfigState(ConfigEntryBase config, bool serverControlled, bool hidden) { return new PackageEntry { kind = PackageEntryKind.ConfigState, section = config.Definition.Section, key = config.Definition.Key, serverControlled = serverControlled, hidden = hidden }; } public static PackageEntry CustomValue(CustomSyncedValueBase customValue, object? value) { return new PackageEntry { kind = PackageEntryKind.CustomValue, key = customValue.Identifier, type = customValue.Type, value = value }; } } private class InvalidDeserializationTypeException : Exception { public string expected = null; public string received = null; public string field = ""; } private enum ConfigPolicyOverride { Default, ForceServerControlled, ForceClientControlled } private enum ModRequirementPolicyOverride { Default, ForceRequired, ForceOptional } private sealed class SyncPolicyRecord { internal readonly string DisplayText; internal readonly string? Key; internal readonly ConfigPolicyOverride PolicyOverride; internal readonly string? Error; internal SyncPolicyRecord(string displayText, string? key, ConfigPolicyOverride policyOverride, string? error) { DisplayText = displayText; Key = key; PolicyOverride = policyOverride; Error = error; } } private sealed class ModRequirementPolicyRecord { internal readonly string DisplayText; internal readonly string? Key; internal readonly ModRequirementPolicyOverride PolicyOverride; internal readonly string? Error; internal ModRequirementPolicyRecord(string displayText, string? key, ModRequirementPolicyOverride policyOverride, string? error) { DisplayText = displayText; Key = key; PolicyOverride = policyOverride; Error = error; } } private sealed class HiddenPolicyRecord { internal readonly string DisplayText; internal readonly string? Key; internal readonly string? Error; internal HiddenPolicyRecord(string displayText, string? key, string? error) { DisplayText = displayText; Key = key; Error = error; } } private enum PolicyTargetFailure { None, Mod, Section, Config } private sealed class PolicyTargetResolution { internal ConditionalConfigSync? ConfigSync; internal readonly List<OwnConfigEntryBase> Configs = new List<OwnConfigEntryBase>(); internal PolicyTargetFailure Failure; } internal static class TerminalInitPatch { internal static void Postfix() { RegisterDebugConsoleCommands(); } } private sealed class SendSlot { internal readonly long Generation; internal readonly ZNet Session; internal readonly bool Sequenced; internal LinkedListNode<SendSlot>? Node; internal bool Waiting = true; internal bool Released; internal SendSlot(long generation, ZNet session, bool sequenced) { Generation = generation; Session = session; Sequenced = sequenced; } } internal readonly struct FullSyncSnapshotKey : IEquatable<FullSyncSnapshotKey> { internal readonly long Revision; internal readonly bool Admin; internal readonly string? ServerVersion; internal readonly int ConfigCount; internal readonly int CustomValueCount; internal readonly bool HasLockingConfig; internal readonly string? LockingConfigSection; internal readonly string? LockingConfigKey; internal readonly ConfigSyncMode LockingConfigSyncMode; internal readonly bool LockingConfigSynchronized; internal readonly bool LockingConfigServerControlled; internal readonly bool LockingConfigHidden; internal FullSyncSnapshotKey(long revision, bool admin, string? serverVersion, int configCount, int customValueCount, OwnConfigEntryBase? lockingConfig) { Revision = revision; Admin = admin; ServerVersion = serverVersion; ConfigCount = configCount; CustomValueCount = customValueCount; HasLockingConfig = lockingConfig != null; LockingConfigSection = ((lockingConfig != null) ? lockingConfig.BaseConfig.Definition.Section : null); LockingConfigKey = ((lockingConfig != null) ? lockingConfig.BaseConfig.Definition.Key : null); LockingConfigSyncMode = lockingConfig?.SyncMode ?? ConfigSyncMode.AlwaysServerControlled; LockingConfigSynchronized = lockingConfig?.SynchronizedConfig ?? false; LockingConfigServerControlled = lockingConfig?.IsServerControlled ?? false; LockingConfigHidden = lockingConfig?.IsHidden ?? false; } public bool Equals(FullSyncSnapshotKey other) { return Revision == other.Revision && Admin == other.Admin && string.Equals(ServerVersion, other.ServerVersion, StringComparison.Ordinal) && ConfigCount == other.ConfigCount && CustomValueCount == other.CustomValueCount && HasLockingConfig == other.HasLockingConfig && string.Equals(LockingConfigSection, other.LockingConfigSection, StringComparison.Ordinal) && string.Equals(LockingConfigKey, other.LockingConfigKey, StringComparison.Ordinal) && LockingConfigSyncMode == other.LockingConfigSyncMode && LockingConfigSynchronized == other.LockingConfigSynchronized && LockingConfigServerControlled == other.LockingConfigServerControlled && LockingConfigHidden == other.LockingConfigHidden; } public override bool Equals(object? obj) { return obj is FullSyncSnapshotKey other && Equals(other); } public override int GetHashCode() { long revision = Revision; int hashCode = revision.GetHashCode(); int num = hashCode * 397; bool admin = Admin; hashCode = num ^ admin.GetHashCode(); hashCode = (hashCode * 397) ^ (ServerVersion?.GetHashCode() ?? 0); hashCode = (hashCode * 397) ^ ConfigCount; hashCode = (hashCode * 397) ^ CustomValueCount; int num2 = hashCode * 397; admin = HasLockingConfig; hashCode = num2 ^ admin.GetHashCode(); hashCode = (hashCode * 397) ^ (LockingConfigSection?.GetHashCode() ?? 0); hashCode = (hashCode * 397) ^ (LockingConfigKey?.GetHashCode() ?? 0); hashCode = (hashCode * 397) ^ (int)LockingConfigSyncMode; int num3 = hashCode * 397; admin = LockingConfigSynchronized; hashCode = num3 ^ admin.GetHashCode(); int num4 = hashCode * 397; admin = LockingConfigServerControlled; hashCode = num4 ^ admin.GetHashCode(); int num5 = hashCode * 397; admin = LockingConfigHidden; return num5 ^ admin.GetHashCode(); } } private sealed class FullSyncSnapshot { internal FullSyncSnapshotKey Key; internal int RawSize; internal int WireSize; internal bool Compressed; internal bool SerializationMeasured; internal bool CompressionMeasured; internal double SerializationMilliseconds; internal double CompressionMilliseconds; internal byte[] WireBytes = Array.Empty<byte>(); } private sealed class PendingSequencedPackage { internal readonly SendSlot Reservation; internal ZPackage? Package; internal PendingSequencedPackage(SendSlot reservation) { Reservation = reservation; } } internal static class ZNetUpdatePatch { internal static void Postfix() { DrainMainThreadQueue(); FlushPendingBroadcastsForAllIfIdle(); } } internal static class ZNetAwakePatch { internal static void Postfix(ZNet __instance) { EnsureDebugSupportInitialized(); isServer = GameReflection.IsServer(__instance); sessionActive = true; if (isServer) { EnsurePolicySupportInitialized(createIfMissing: true); } ConditionalConfigSync[] array = configSyncs.ToArray(); foreach (ConditionalConfigSync conditionalConfigSync in array) { conditionalConfigSync.RegisterForActiveSession(); } if (isServer) { GameReflection.StartCoroutine(WatchAdminListChanges(), (MonoBehaviour?)(object)__instance); } static void SendAdmin(List<ZNetPeer> peers, bool isAdmin) { ConditionalConfigSync[] array2 = configSyncs.ToArray(); foreach (ConditionalConfigSync configSync in array2) { configSync.StartBroadcastPackage(peers, () => configSync.ConfigsToPackage(null, null, new PackageEntry[1] { PackageEntry.LockExempt(isAdmin) }), sequenced: false, null); } } IEnumerator WatchAdminListChanges() { SyncedList adminList = GameReflection.GetAdminList(__instance) ?? throw new InvalidOperationException("ZNet admin list is unavailable."); List<string> currentList = GameReflection.GetSyncedListValues(adminList); while (true) { yield return (object)new WaitForSeconds(30f); if (!sessionActive || GameReflection.ZNetInstance != __instance) { break; } if (!GameReflection.GetSyncedListValues(adminList).SequenceEqual(currentList)) { currentList = GameReflection.GetSyncedListValues(adminList); List<ZNetPeer> peers = GameReflection.GetPeers(__instance); List<ZNetPeer> adminPeer = peers.Where(IsPeerAdmin).ToList(); List<ZNetPeer> nonAdminPeer = peers.Except(adminPeer).ToList(); ConditionalConfigSync[] array2 = configSyncs.ToArray(); foreach (ConditionalConfigSync sync in array2) { sync.DebugLog(ConditionalConfigSyncDebugLevel.Basic, "Admin", $"Admin list changed, admins={adminPeer.Count}, nonAdmins={nonAdminPeer.Count}"); } SendAdmin(nonAdminPeer, isAdmin: false); SendAdmin(adminPeer, isAdmin: true); } } } } } internal static class ZNetOnNewConnectionPatch { internal static void Postfix(ZNet __instance, ZNetPeer peer) { if (!GameReflection.IsServer(__instance)) { ConditionalConfigSync[] array = configSyncs.ToArray(); foreach (ConditionalConfigSync conditionalConfigSync in array) { conditionalConfigSync.RegisterClientRpcHandler(peer); } } } } internal static class ZNetShutdownPatch { internal static void Postfix() { ConditionalConfigSync[] array = configSyncs.ToArray(); sessionActive = false; ProcessingServerUpdate = true; lockExempt = false; ConditionalConfigSync[] array2 = array; foreach (ConditionalConfigSync conditionalConfigSync in array2) { conditionalConfigSync.ResetTransportState(); } try { ConditionalConfigSync[] array3 = array; foreach (ConditionalConfigSync conditionalConfigSync2 in array3) { try { conditionalConfigSync2.DebugLog(ConditionalConfigSyncDebugLevel.Basic, "Shutdown", "Reset local values and source-of-truth state"); conditionalConfigSync2.InitialSyncDone = false; conditionalConfigSync2.ResetConfigsFromServer(); conditionalConfigSync2.IsSourceOfTruth = true; conditionalConfigSync2.ServerLockedSettingChanged(); } catch (Exception arg) { conditionalConfigSync2.DebugWarning("Shutdown", $"Failed to reset one synchronization instance; continuing. Error: {arg}"); } } } finally { ResetNetworkSessionState(); isServer = false; ProcessingServerUpdate = false; } ConditionalConfigSync[] array4 = array; foreach (ConditionalConfigSync conditionalConfigSync3 in array4) { conditionalConfigSync3.InvokeEventHandlers(conditionalConfigSync3.ServerConnectionReset, "ServerConnectionReset"); } } } internal static class ZNetRpcPeerInfoSyncPatch { internal class BufferingSocket : ZPlayFabSocket, ISocket { public volatile bool finished = false; public volatile int versionMatchQueued = -1; public readonly List<ZPackage> Package = new List<ZPackage>(); public readonly ISocket Original; internal BufferingSocket(ISocket original) { Original = original; } public bool IsConnected() { return GameReflection.SocketIsConnected(Original); } public ZPackage Recv() { return GameReflection.SocketRecv(Original); } public int GetSendQueueSize() { return GameReflection.SocketGetSendQueueSize(Original); } public int GetCurrentSendRate() { return GameReflection.SocketGetCurrentSendRate(Original); } public bool IsHost() { return GameReflection.SocketIsHost(Original); } public void Dispose() { GameReflection.SocketDispose(Original); } public bool GotNewData() { return GameReflection.SocketGotNewData(Original); } public void Close() { GameReflection.SocketClose(Original); } public string GetEndPointString() { return GameReflection.SocketGetEndPointString(Original); } public void GetAndResetStats(out int totalSent, out int totalRecv) { GameReflection.SocketGetAndResetStats(Original, out totalSent, out totalRecv); } public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec) { GameReflection.SocketGetConnectionQuality(Original, out localQuality, out remoteQuality, out ping, out outByteSec, out inByteSec); } public ISocket Accept() { return GameReflection.SocketAccept(Original); } public int GetHostPort() { return GameReflection.SocketGetHostPort(Original); } public bool Flush() { return GameReflection.SocketFlush(Original); } public string GetHostName() { return GameReflection.SocketGetHostName(Original); } public void VersionMatch() { if (finished) { GameReflection.SocketVersionMatch(Original); } else { versionMatchQueued = Package.Count; } } private static bool ShouldBufferInitialPackage(int methodHash) { return methodHash == GameReflection.StableHash("PeerInfo") || methodHash == GameReflection.StableHash("PlayerList") || methodHash == GameReflection.StableHash("HistoricalPlayerList") || methodHash == GameReflection.StableHash("AdminList") || methodHash == GameReflection.StableHash("RoutedRPC") || methodHash == GameReflection.StableHash("ZDOData") || methodHash == GameReflection.StableHash("ConditionalConfigSync RemoteConsumerSnapshot"); } public void Send(ZPackage pkg) { int position = GameReflection.PackageGetPos(pkg); GameReflection.PackageSetPos(pkg, 0); int methodHash = GameReflection.PackageReadInt(pkg); GameReflection.PackageSetPos(pkg, position); if (!finished && ShouldBufferInitialPackage(methodHash)) { ZPackage val = GameReflection.NewPackage(GameReflection.PackageGetArray(pkg)); GameReflection.PackageSetPos(val, position); Package.Add(val); } else { GameReflection.SocketSend(Original, pkg); } } } internal static void Prefix(ref BufferingSocket? __state, ZNet __instance, ZRpc rpc) { if (!GameReflection.IsServer(__instance)) { return; } BufferingSocket bufferingSocket = new BufferingSocket(GameReflection.GetRpcSocket(rpc) ?? throw new InvalidOperationException("RPC socket is unavailable.")); GameReflection.SetRpcSocket(rpc, (ISocket)(object)bufferingSocket); ZNetPeer peer = GameReflection.GetPeer(rpc, __instance); if (peer != null && !object.Equals(GameReflection.GetOnlineBackend(), (object)(OnlineBackendType)0)) { ISocket? peerSocket = GameReflection.GetPeerSocket(peer); ZPlayFabSocket val = (ZPlayFabSocket)(object)((peerSocket is ZPlayFabSocket) ? peerSocket : null); if (val != null) { GameReflection.SetPlayFabRemotePlayerId((ZPlayFabSocket)(object)bufferingSocket, GameReflection.GetPlayFabRemotePlayerId(val)); } GameReflection.SetPeerSocket(peer, (ISocket)(object)bufferingSocket); } __state = bufferingSocket; } internal static void Postfix(BufferingSocket? __state, ZNet __instance, ZRpc rpc) { if (!GameReflection.IsServer(__instance) || __state == null) { return; } ZNetPeer peer = GameReflection.GetPeer(rpc, __instance); if (peer == null) { SendBufferedData(); return; } KeyValuePair<ConditionalConfigSync, SendSlot?>[] initialSends = (from sync in configSyncs.ToArray() select new KeyValuePair<ConditionalConfigSync, SendSlot>(sync, sync.ReserveSendSlot())).ToArray(); try { if (GameReflection.StartCoroutine(sendAsync(), (MonoBehaviour?)(object)__instance) == null) { ReleaseInitialSends(); SendBufferedData(); } } catch { ReleaseInitialSends(); SendBufferedData(); throw; } void ReleaseInitialSends() { KeyValuePair<ConditionalConfigSync, SendSlot>[] array = initialSends; for (int i = 0; i < array.Length; i++) { KeyValuePair<ConditionalConfigSync, SendSlot> keyValuePair = array[i]; if (keyValuePair.Value != null) { keyValuePair.Key.ReleaseSendSlot(keyValuePair.Value); } } } void SendBufferedData() { BufferingSocket bufferingSocket = __state; if ((object)GameReflection.GetRpcSocket(rpc) == bufferingSocket) { GameReflection.SetRpcSocket(rpc, bufferingSocket.Original); } ZNetPeer peer2 = GameReflection.GetPeer(rpc, __instance); if (peer2 != null && (object)GameReflection.GetPeerSocket(peer2) == bufferingSocket) { GameReflection.SetPeerSocket(peer2, bufferingSocket.Original); } if (bufferingSocket.finished) { return; } bufferingSocket.finished = true; try { if (sessionActive && GameReflection.ZNetInstance == __instance && GameReflection.SocketIsConnected(bufferingSocket.Original)) { for (int i = 0; i < bufferingSocket.Package.Count; i++) { if (i == bufferingSocket.versionMatchQueued) { GameReflection.SocketVersionMatch(bufferingSocket.Original); } GameReflection.SocketSend(bufferingSocket.Original, bufferingSocket.Package[i]); } if (bufferingSocket.Package.Count == bufferingSocket.versionMatchQueued) { GameReflection.SocketVersionMatch(bufferingSocket.Original); } } } finally { bufferingSocket.Package.Clear(); bufferingSocket.versionMatchQueued = -1; } } IEnumerator sendAsync() { try { KeyValuePair<ConditionalConfigSync, SendSlot?>[] array = initialSends; for (int i = 0; i < array.Length; i++) { KeyValuePair<ConditionalConfigSync, SendSlot?> entry = array[i]; if (entry.Value != null && entry.Key.IsCurrentSend(entry.Value)) { yield return GameReflection.StartCoroutine(entry.Key.SendFullSyncPackage(peer, "InitialSync", "Sending full sync to", entry.Value), (MonoBehaviour?)(object)__instance); } } } finally { ReleaseInitialSends(); SendBufferedData(); } } } } [Description("True while an incoming synchronization package is being applied. Usually diagnostic only.")] public static bool ProcessingServerUpdate = false; [Description("Stable unique synchronization identifier, normally the owning mod GUID.")] public readonly string Name; [Description("Human-readable mod name used in logs.")] public string? DisplayName; [Description("Current version of the owning mod used for peer compatibility checks.")] public string? CurrentVersion; [Description("Oldest compatible remote mod version accepted by this instance.")] public string? MinimumRequiredVersion; [Description("Allows unlocked non-admin clients to publish changes. Disabled by default.")] public bool AllowClientConfigUpdatesWhenUnlocked = false; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private static readonly HashSet<ConditionalConfigSync> configSyncs = new HashSet<ConditionalConfigSync>(); private readonly HashSet<OwnConfigEntryBase> allConfigs = new HashSet<OwnConfigEntryBase>(); private readonly HashSet<ConfigFile> observedConfigFiles = new HashSet<ConfigFile>(); private HashSet<CustomSyncedValueBase> allCustomValues = new HashSet<CustomSyncedValueBase>(); private static bool isServer; private static bool lockExempt = false; private OwnConfigEntryBase? lockedConfig = null; private bool? lastNotifiedLockState; private static readonly Regex assemblyVersionTypeIdentityPattern = new Regex(",\\s*Version=[^,\\]\\[]+", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); [Description("Enables shared ConditionalConfigSync diagnostic logging programmatically.")] public static bool DebugLoggingEnabled = false; [Description("Selects diagnostic logging verbosity.")] public static ConditionalConfigSyncDebugLevel DebugLoggingLevel = ConditionalConfigSyncDebugLevel.Basic; [Description("Optional case-insensitive filter matched against mod sync names and display names.")] public static string DebugLoggingFilter = ""; private static bool debugConfigEnabled; private static ConditionalConfigSyncDebugLevel debugConfigLevel = ConditionalConfigSyncDebugLevel.Basic; private static string debugConfigFilter = ""; private static bool debugSupportInitialized; private static bool debugConfigReloadScheduled; private static bool debugCommandsRegistered; private static FileSystemWatcher? debugConfigWatcher; private static readonly object debugSupportLock = new object(); private const int largeStringCustomValueWarningBytes = 131072; private readonly HashSet<string> largeStringCustomValueWarnings = new HashSet<string>(StringComparer.Ordinal); private const string SyncPolicyFileName = "ConditionalConfigSync.SyncPolicy.cfg"; private const string HiddenConfigsFileName = "ConditionalConfigSync.HiddenConfigs.cfg"; private const string ModRequirementsFileName = "ConditionalConfigSync.ModRequirements.cfg"; private const string PolicyDumpFileName = "ConditionalConfigSync.PolicyDump.txt"; private static readonly object policyLock = new object(); private static bool policySupportInitialized; private static bool policyReloadScheduled; private static long policyReadGeneration; private static long policyAppliedGeneration; private static FileSystemWatcher? syncPolicyWatcher; private static FileSystemWatcher? hiddenConfigsWatcher; private static FileSystemWatcher? modRequirementsWatcher; private static Dictionary<string, ConfigPolicyOverride> syncPolicy = new Dictionary<string, ConfigPolicyOverride>(StringComparer.OrdinalIgnoreCase); private static HashSet<string> hiddenConfigPolicy = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static Dictionary<string, ModRequirementPolicyOverride> modRequirementPolicy = new Dictionary<string, ModRequirementPolicyOverride>(StringComparer.OrdinalIgnoreCase); private const string PolicyChangeRpcSuffix = " ConditionalConfigSync Policy Change"; private const int MaxPolicyChangeRequestSize = 4096; private const int MaxPolicyIdentifierLength = 1024; private const int PolicyChangeCapability = 1; private bool remotePolicyChangeSupported; private static ManualLogSource? logSource; private static readonly object runtimeLock = new object(); private static bool runtimeInitialized; private static int mainThreadId; private static readonly object mainThreadQueueLock = new object(); private static readonly Queue<Action> mainThreadQueue = new Queue<Action>(); private static long mainThreadQueueGeneration; private const string ConfigDirectoryName = "shudnal.ConditionalConfigSync"; private const string DebugConfigFileName = "ConditionalConfigSync.Debug.cfg"; private static readonly object configDirectoryLock = new object(); private static bool configDirectoryInitialized; private readonly LinkedList<SendSlot> sendQueue = new LinkedList<SendSlot>(); private SendSlot? pendingStateSendSlot; private long transportGeneration; private int waitingSequencedSendCount; private bool initialSyncRepairRequested; private static int packagePreparationDepth; private static bool flushingAllPendingBroadcasts; private const string ResyncRpcSuffix = " ConditionalConfigSync Resync"; private const int StableReadAttempts = 5; private const int StableReadDelayMilliseconds = 50; private const int FullSyncSnapshotBuildAttempts = 5; private const int FullSyncSnapshotStableFrames = 2; private readonly VersionCheck versionCheck; private bool serverRpcsRegistered; private readonly HashSet<ZRpc> registeredClientRpcs = new HashSet<ZRpc>(); private readonly HashSet<OwnConfigEntryBase> lateRegisteredConfigs = new HashSet<OwnConfigEntryBase>(); private readonly HashSet<CustomSyncedValueBase> lateRegisteredCustomValues = new HashSet<CustomSyncedValueBase>(); private bool lateRegistrationSyncScheduled; private long fullSyncStateRevision = 1L; private FullSyncSnapshot? fullSyncAdminSnapshot; private FullSyncSnapshot? fullSyncNonAdminSnapshot; private static bool sessionActive; private static Harmony? runtimeHarmony; private int processingCount; private bool lastHandledPackageWasFull; private readonly HashSet<ConfigEntryBase> pendingConfigBroadcasts = new HashSet<ConfigEntryBase>(); private readonly HashSet<CustomSyncedValueBase> pendingCustomValueBroadcasts = new HashSet<CustomSyncedValueBase>(); private readonly LinkedList<PendingSequencedPackage> pendingSequencedCustomValuePackages = new LinkedList<PendingSequencedPackage>(); private bool flushingPendingBroadcasts; private readonly HashSet<ConfigEntryBase> configsBeingApplied = new HashSet<ConfigEntryBase>(); private readonly HashSet<CustomSyncedValueBase> customValuesBeingApplied = new HashSet<CustomSyncedValueBase>(); private readonly Dictionary<long, long> authoritativeCorrectionTimes = new Dictionary<long, long>(); private static readonly long AuthoritativeCorrectionIntervalTicks = TimeSpan.FromSeconds(1.0).Ticks; private const byte PARTIAL_CONFIGS = 1; private const byte FRAGMENTED_CONFIG = 2; private const byte COMPRESSED_CONFIG = 4; private const byte V2_PACKAGE = 8; private const int packageSliceSize = 250000; private const int maximumSendQueueSize = 20000; private const int compressMinSize = 10000; private const int maxPackageEntries = 8192; private const int maxPayloadSize = 20971520; private const int maxFragments = 128; private const int maxFragmentSize = 300000; private const int maxFragmentAssembliesPerSender = 4; private const int maxFragmentCacheBytesPerSender = 20971520; private const int maxFragmentCacheBytesGlobal = 67108864; private const int maxPendingSequencedUpdates = 100; private readonly Dictionary<string, SortedDictionary<int, byte[]>> configValueCache = new Dictionary<string, SortedDictionary<int, byte[]>>(); private readonly Dictionary<string, int> configValueCacheExpectedFragments = new Dictionary<string, int>(); private readonly Dictionary<string, int> configValueCacheBytes = new Dictionary<string, int>(); private readonly Dictionary<string, long> configValueCacheSenders = new Dictionary<string, long>(); private readonly List<KeyValuePair<long, string>> cacheExpirations = new List<KeyValuePair<long, string>>(); private static long packageCounter = 0L; [Description("Author-defined default for whether the owning mod must exist compatibly on the remote peer.")] public bool ModRequired { get; set; } = false; [Description("Whether the server may override the author-defined ModRequired default for incoming clients.")] public ModRequirementMode ModRequirementMode { get; set; } = ModRequirementMode.Fixed; private bool ServerLockEnabled => forceConfigLocking ?? (lockedConfig != null && ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0); [Description("Effective local lock state. Unlocked non-admin writes still require explicit mod opt-in.")] public bool IsLocked { get { return ServerLockEnabled && !lockExempt; } set { bool isLocked = IsLocked; forceConfigLocking = value; if (isLocked != IsLocked) { ServerLockedSettingChanged(); } } } [Description("Whether the local side is the source of truth or has a server admin exemption.")] public bool IsAdmin => lockExempt || isSourceOfTruth; [Description("Whether the local side currently owns and publishes synchronized values.")] public bool IsSourceOfTruth { get { return isSourceOfTruth; } private set { if (value == isSourceOfTruth) { return; } isSourceOfTruth = value; foreach (CustomSyncedValueBase allCustomValue in allCustomValues) { allCustomValue.SetLocalOwnership(value); } ServerLockedSettingChanged(); InvokeEventHandlers(this.SourceOfTruthChanged, value, "SourceOfTruthChanged"); } } [Description("Whether initial server synchronization has completed for this mod instance.")] public bool InitialSyncDone { get; private set; } = false; private bool CanPublishConfigStateNotifications => !IsProcessing && configsBeingApplied.Count == 0 && customValuesBeingApplied.Count == 0; private static bool IsDebugActive => DebugLoggingEnabled || debugConfigEnabled; private static ConditionalConfigSyncDebugLevel EffectiveDebugLevel { get { int num = 0; if (DebugLoggingEnabled) { num = Math.Max(num, (int)DebugLoggingLevel); } if (debugConfigEnabled) { num = Math.Max(num, (int)debugConfigLevel); } return (num <= 0) ? ConditionalConfigSyncDebugLevel.Basic : ((ConditionalConfigSyncDebugLevel)num); } } private static string EffectiveDebugFilter => (!string.IsNullOrWhiteSpace(DebugLoggingFilter)) ? DebugLoggingFilter : debugConfigFilter; private static string SyncPolicyPath => Path.Combine(ConfigDirectoryPath, "ConditionalConfigSync.SyncPolicy.cfg"); private static string HiddenConfigsPath => Path.Combine(ConfigDirectoryPath, "ConditionalConfigSync.HiddenConfigs.cfg"); private static string ModRequirementsPath => Path.Combine(ConfigDirectoryPath, "ConditionalConfigSync.ModRequirements.cfg"); private static ManualLogSource LogSource => logSource ?? (logSource = Logger.CreateLogSource("ConditionalConfigSync")); private static string ConfigDirectoryPath => Path.Combine(Paths.ConfigPath, "shudnal.ConditionalConfigSync"); private static string DebugConfigPath => Path.Combine(ConfigDirectoryPath, "ConditionalConfigSync.Debug.cfg"); private static bool IsMainThread => mainThreadId == 0 || Thread.CurrentThread.ManagedThreadId == mainThreadId; private bool IsSending => sendQueue.Count > 0; private bool IsProcessing => processingCount > 0; private bool ShouldDeferOutgoingBroadcasts => ProcessingServerUpdate || packagePreparationDepth > 0 || IsProcessing || IsSending || flushingPendingBroadcasts; [Description("Raised when this process changes between authoritative and client-replica roles.")] public event Action<bool>? SourceOfTruthChanged; [Description("Raised after the first complete server synchronization has been applied on a client.")] public event Action? InitialSyncCompleted; [Description("Raised after server state is cleared and local fallback values are restored.")] public event Action? ServerConnectionReset; [Description("Raised for any effective policy-state transition of a registered config entry.")] public event EventHandler<PolicyStateChangedEventArgs>? PolicyStateChanged; [Description("Raised when a config entry changes between server-controlled and client-controlled.")] public event Action<OwnConfigEntryBase, bool>? ServerControlledChanged; [Description("Raised when a config entry changes between hidden and visible.")] public event Action<OwnConfigEntryBase, bool>? HiddenStateChanged; [Description("Raised when the effective configuration lock changes.")] public event Action<bool>? LockStateChanged; [Description("Raised when synchronization is rejected for permissions, safety limits, malformed data, or an exception.")] public event EventHandler<SyncRejectedEventArgs>? SyncRejected; private event Action? LockedConfigChanged; [Description("Creates one synchronization instance. Pass a stable mod GUID as the name.")] public ConditionalConfigSync(string name) { EnsureRuntimeReady(); Name = name; configSyncs.Add(this); versionCheck = new VersionCheck(this); if (sessionActive) { QueueMainThread(RegisterForActiveSession); } } [Description("Registers a policy-controlled config that is server-controlled by default.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry) { return AddConfigEntry<T>(configEntry, ConfigSyncMode.Conditional, serverControlledByDefault: true); } [Description("Registers a Conditional config with an explicit server/client default.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry, bool synchronizedSetting) { return AddConfigEntry<T>(configEntry, ConfigSyncMode.Conditional, synchronizedSetting); } [Description("Registers a config with an explicit synchronization mode.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry, ConfigSyncMode syncMode) { return AddConfigEntry<T>(configEntry, syncMode, serverControlledByDefault: true); } [Description("Registers a fully configured config without a temporary synchronization state.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry, ConfigSyncMode syncMode, bool serverControlledByDefault) { //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Expected O, but got Unknown if (!Enum.IsDefined(typeof(ConfigSyncMode), syncMode)) { throw new ArgumentOutOfRangeException("syncMode", syncMode, "Unknown config synchronization mode."); } bool flag = syncMode switch { ConfigSyncMode.AlwaysServerControlled => true, ConfigSyncMode.AlwaysClientControlled => false, _ => serverControlledByDefault, }; if (GetConfigData((ConfigEntryBase)(object)configEntry) is SyncedConfigEntry<T> result) { return result; } ConfigDefinition definition = ((ConfigEntryBase)configEntry).Definition; if (allConfigs.Any((OwnConfigEntryBase config) => config.BaseConfig.Definition.Equals(definition))) { throw new InvalidOperationException("Config entry '" + definition.Section + " -> " + definition.Key + "' is already registered in sync '" + Name + "'."); } SyncedConfigEntry<T> syncedConfigEntry = new SyncedConfigEntry<T>(configEntry) { SyncMode = syncMode, SynchronizedConfig = flag }; syncedConfigEntry.StoreLastAcceptedValue(((ConfigEntryBase)configEntry).BoxedValue); ConfigDescription description = ((ConfigEntryBase)configEntry).Description; object[] array = new object[1] { new ConfigurationManagerAttributes() }.Concat((description.Tags ?? Array.Empty<object>()).Where((object tag) => !(tag is OwnConfigEntryBase) && !(tag is ConfigurationManagerAttributes))).Concat(new SyncedConfigEntry<T>[1] { syncedConfigEntry }).ToArray(); ConfigDescription value = new ConfigDescription(description.Description, description.AcceptableValues, array); (AccessTools.DeclaredField(typeof(ConfigEntryBase), "<Description>k__BackingField") ?? throw new MissingFieldException(typeof(ConfigEntryBase).FullName, "<Description>k__BackingField")).SetValue(configEntry, value); allConfigs.Add(syncedConfigEntry); if (!observedConfigFiles.Add(((ConfigEntryBase)configEntry).ConfigFile)) { ((ConfigEntryBase)configEntry).ConfigFile.SettingChanged -= OnObservedConfigFileChanged; } ((ConfigEntryBase)configEntry).ConfigFile.SettingChanged += OnObservedConfigFileChanged; InvalidateFullSyncSnapshot("registered config: " + definition.Section + " -> " + definition.Key); bool flag2 = IsSourceOfTruth && isServer && GameReflection.HasZNet && policySupportInitialized; syncedConfigEntry.IsServerControlled = (flag2 ? ComputeServerControlled(syncedConfigEntry) : GetDefaultServerControlled(syncedConfigEntry)); syncedConfigEntry.IsHidden = flag2 && ComputeHidden(syncedConfigEntry); syncedConfigEntry.IsPolicyStateInitialized = flag2; ConfigurationManagerAttributes configAttribute = GetConfigAttribute<ConfigurationManagerAttributes>((ConfigEntryBase)(object)configEntry); configAttribute.ReadOnly = !IsWritableConfig(syncedConfigEntry); configAttribute.Browsable = !syncedConfigEntry.IsHidden; DebugLog(ConditionalConfigSyncDebugLevel.Trace, "Register", string.Format("Added config {0}/{1}, type={2}, mode={3}, default={4}", definition.Section, definition.Key, ((ConfigEntryBase)configEntry).SettingType.Name, syncMode, flag ? "ServerControlled" : "ClientControlled")); ScheduleLateRegistrationSync(syncedConfigEntry); return syncedConfigEntry; } private void OnObservedConfigFileChanged(object? sender, SettingChangedEventArgs args) { OwnConfigEntryBase configData = GetConfigData(args.ChangedSetting); if (configData != null && allConfigs.Contains(configData)) { OnObservedConfigChange(args.ChangedSetting, configData); } } private void OnObservedConfigChange(ConfigEntryBase configEntry, OwnConfigEntryBase data) { try { OnConfigEntryChanged(configEntry, data); } finally { if (data == lockedConfig) { this.LockedConfigChanged?.Invoke(); } } } [Description("Binds and registers a Conditional config with an explicit server/client default.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigFile configFile, string section, string key, T defaultValue, string description, bool synchronizedSetting) { return AddConfigEntry<T>(configFile.Bind<T>(section, key, defaultValue, description), ConfigSyncMode.Conditional, synchronizedSetting); } [Description("Binds and registers a config in one call using a text description.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigFile configFile, string section, string key, T defaultValue, string description, ConfigSyncMode syncMode = ConfigSyncMode.Conditional, bool serverControlledByDefault = true) { return AddConfigEntry<T>(configFile.Bind<T>(section, key, defaultValue, description), syncMode, serverControlledByDefault); } [Description("Binds and registers a Conditional ConfigDescription entry with an explicit server/client default.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigFile configFile, string section, string key, T defaultValue, ConfigDescription description, bool synchronizedSetting) { return AddConfigEntry<T>(configFile.Bind<T>(section, key, defaultValue, description), ConfigSyncMode.Conditional, synchronizedSetting); } [Description("Binds and registers a config in one call using a ConfigDescription.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigFile configFile, string section, string key, T defaultValue, ConfigDescription description, ConfigSyncMode syncMode = ConfigSyncMode.Conditional, bool serverControlledByDefault = true) { return AddConfigEntry<T>(configFile.Bind<T>(section, key, defaultValue, description), syncMode, serverControlledByDefault); } [Description("Binds and registers a Conditional ConfigDefinition entry with a text description and explicit default.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigFile configFile, ConfigDefinition definition, T defaultValue, string description, bool synchronizedSetting) { return AddConfigEntry<T>(configFile.Bind<T>(definition.Section, definition.Key, defaultValue, description), ConfigSyncMode.Conditional, synchronizedSetting); } [Description("Binds and registers a ConfigDefinition entry with a text description.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigFile configFile, ConfigDefinition definition, T defaultValue, string description, ConfigSyncMode syncMode = ConfigSyncMode.Conditional, bool serverControlledByDefault = true) { return AddConfigEntry<T>(configFile.Bind<T>(definition.Section, definition.Key, defaultValue, description), syncMode, serverControlledByDefault); } [Description("Binds and registers a Conditional ConfigDefinition entry with an explicit server/client default.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigFile configFile, ConfigDefinition definition, T defaultValue, ConfigDescription description, bool synchronizedSetting) { return AddConfigEntry<T>(configFile.Bind<T>(definition.Section, definition.Key, defaultValue, description), ConfigSyncMode.Conditional, synchronizedSetting); } [Description("Binds and registers a config in one call using a ConfigDefinition.")] public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigFile configFile, ConfigDefinition definition, T defaultValue, ConfigDescription description, ConfigSyncMode syncMode = ConfigSyncMode.Conditional, bool serverControlledByDefault = true) { return AddConfigEntry<T>(configFile.Bind<T>(definition.Section, definition.Key, defaultValue, description), syncMode, serverControlledByDefault); } [Description("Registers the protected server-controlled lock entry. Non-admin clients can never change it.")] public SyncedConfigEntry<T> AddLockingConfigEntry<T>(ConfigEntry<T> lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry<T>(lockingConfig, ConfigSyncMode.AlwaysServerControlled); lockedConfig.SyncMode = ConfigSyncMode.AlwaysServerControlled; lockedConfig.SynchronizedConfig = true; lockedConfig.IsServerControlled = true; lockedConfig.IsPolicyStateInitialized = IsSourceOfTruth && isServer && GameReflection.HasZNet && policySupportInitialized; lockedConfig.StoreLastAcceptedValue(((ConfigEntryBase)lockingConfig).BoxedValue); LockedConfigChanged -= ServerLockedSettingChanged; LockedConfigChanged += ServerLockedSettingChanged; InvalidateFullSyncSnapshot("registered locking config"); ServerLockedSettingChanged(); ScheduleLateRegistrationSync(lockedConfig); return (SyncedConfigEntry<T>)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { if (allCustomValues.Select((CustomSyncedValueBase v) => v.Identifier).Concat(new string[2] { "serverversion", "lockexempt" }).Contains(customValue.Identifier)) { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion, lockexempt)"); } allCustomValues.Add(customValue); allCustomValues = new HashSet<CustomSyncedValueBase>(from v in allCustomValues orderby v.Priority descending, v.RegistrationIndex select v); InvalidateFullSyncSnapshot("registered custom value: " + customValue.Identifier); DebugLog(ConditionalConfigSyncDebugLevel.Trace, "Register", $"Added custom value {customValue.Identifier}, type={customValue.Type.Name}, priority={customValue.Priority}, sequenced={customValue.PreserveUpdateSequence}"); ScheduleLateRegistrationSync(null, customValue); } internal void ReportCustomValueSubscriberFailure(string identifier, Exception exception) { DebugWarning("CustomValue", $"ValueChanged subscriber for '{identifier}' failed; continuing with the remaining subscribers. Error: {exception}"); } internal void ReportCustomValuePublicationFailure(string identifier, Exception exception) { DebugWarning("CustomValue", $"Failed to publish custom value '{identifier}' after its notification callbacks completed. Error: {exception}"); } [Description("Gets the compatibility state of a CCS consumer on a connected routed peer UID.")] public static RemoteConsumerState GetRemoteConsumerState(string consumerGuid, long peerUid) { if (string.IsNullOrWhiteSpace(consumerGuid) || peerUid == 0) { return RemoteConsumerState.Unknown; } return VersionCheck.GetRemoteConsumerState(consumerGuid.Trim(), peerUid); } [Description("Returns whether a connected routed peer has a compatible CCS consumer.")] public static bool HasCompatibleConsumer(string consumerGuid, long peerUid) { return GetRemoteConsumerState(consumerGuid, peerUid) == RemoteConsumerState.Compatible; } internal bool CanAcceptActiveCustomValueChange(CustomSyncedValueBase customValue) { if (customValuesBeingApplied.Contains(customValue) || IsSourceOfTruth || CanBroadcastFromThisSide()) { return true; } string text = ((!InitialSyncDone) ? "initial server synchronization is not complete" : "the local side is not authorized to publish active custom values"); DebugWarning("CustomValue", "Rejected local change to protected custom value " + customValue.Identifier + ": " + text); return false; } internal static bool IsWritableConfig(OwnConfigEntryBase config) { ConditionalConfigSync conditionalConfigSync = configSyncs.FirstOrDefault((ConditionalConfigSync cs) => cs.allConfigs.Contains(config)); if (conditionalConfigSync == null) { return true; } if (conditionalConfigSync.IsSourceOfTruth || !config.IsServerControlled) { return true; } if (!conditionalConfigSync.InitialSyncDone) { return false; } if (config == conditionalConfigSync.lockedConfig) { return conditionalConfigSync.IsAdmin; } if (conditionalConfigSync.IsAdmin) { return true; } if (conditionalConfigSync.ServerLockEnabled) { return false; } return conditionalConfigSync.AllowClientConfigUpdatesWhenUnlocked; } internal static bool ShouldStoreLocalConfigValue(OwnConfigEntryBase config) { ConditionalConfigSync owningConfigSync = GetOwningConfigSync(config); return owningConfigSync != null && !owningConfigSync.IsSourceOfTruth && config.IsServerControlled; } private string GetWriteRejectionReason(OwnConfigEntryBase config) { if (!InitialSyncDone) { return "initial server synchronization is not complete"; } if (config == lockedConfig) { return "the protected locking config requires administrator access"; } if (ServerLockEnabled) { return "the server configuration is locked"; } if (!AllowClientConfigUpdatesWhenUnlocked) { return "unlocked client updates are disabled by the mod"; } return "the local side is not authorized to change this server-controlled config"; } private void ServerLockedSettingChanged() { UpdateConfigMetadata(); if (CanPublishConfigStateNotifications) { RaiseLockStateChangedIfNeeded(); } } private void UpdateConfigMetadata() { DebugLog(ConditionalConfigSyncDebugLevel.Verbose, "Lock", $"Re-evaluating read-only state: serverLock={ServerLockEnabled}, effectiveLock={IsLocked}, admin={IsAdmin}, sourceOfTruth={IsSourceOfTruth}, initialSync={InitialSyncDone}, unlockedClientUpdates={AllowClientConfigUpdatesWhenUnlocked}"); foreach (OwnConfigEntryBase allConfig in allConfigs) { ConfigurationManagerAttributes configAttribute = GetConfigAttribute<ConfigurationManagerAttributes>(allConfig.BaseConfig); configAttribute.ReadOnly = !IsWritableConfig(allConfig); configAttribute.Browsable = !allConfig.IsHidden; } } private void RestoreRejectedConfigChange(ConfigEntryBase configEntry, OwnConfigEntryBase syncedEntry, string reason) { object boxedValue = (syncedEntry.HasServerValue ? syncedEntry.ServerValue : (syncedEntry.HasLastAcceptedValue ? syncedEntry.LastAcceptedValue : (syncedEntry.HasLocalBaseValue ? syncedEntry.LocalBaseValue : configEntry.DefaultValue))); ConfigFile configFile = configEntry.ConfigFile; bool saveOnConfigSet = configFile.SaveOnConfigSet; configFile.SaveOnConfigSet = false; configsBeingApplied.Add(configEntry); try { configEntry.BoxedValue = boxedValue; syncedEntry.StoreLastAcceptedValue(configEntry.BoxedValue); } catch (Exception arg) { ConfigDefinition definition = configEntry.Definition; DebugWarning("ConfigUpdate", $"Failed to restore protected config {definition.Section} -> {definition.Key} after a rejected local change. Error: {arg}"); return; } finally { configsBeingApplied.Remove(configEntry); configFile.SaveOnConfigSet = saveOnConfigSet; } try { configFile.Save(); } catch (Exception ex) { DebugWarning("ConfigUpdate", $"Failed to save local fallback after restoring {configEntry.Definition}: {ex.Message}"); } DebugWarning("ConfigUpdate", "Restored protected config " + configEntry.Definition.Section + " -> " + configEntry.Definition.Key + " after a rejected local change: " + reason); } private void ResetConfigsFromServer(ParsedConfigs? retainedSnapshot = null) { remotePolicyChangeSupported = false; DebugLog(ConditionalConfigSyncDebugLevel.Verbose, "Reset", "Restoring local values after server sync/session end"); Dictionary<ConfigFile, bool> dictionary = new Dictionary<ConfigFile, bool>(); List<PolicyStateChangedEventArgs> list = new List<PolicyStateChangedEventArgs>(); if (retainedSnapshot == null) { ClearPendingBroadcasts(); } bool flag = retainedSnapshot == null && !isSourceOfTruth; if (flag) { isSourceOfTruth = true; foreach (CustomSyncedValueBase allCustomValue in allCustomValues) { allCustomValue.SetLocalOwnership(isOwner: true); } } OwnConfigEntryBase[] array = allConfigs.Where((OwnConfigEntryBase config) => retainedSnapshot == null || (!retainedSnapshot.configValues.ContainsKey(config) && !retainedSnapshot.configStates.ContainsKey(config))).ToArray(); CustomSyncedValueBase[] source = allCustomValues.Where((CustomSyncedValueBase config) => retainedSnapshot == null || !retainedSnapshot.customValues.ContainsKey(config)).ToArray(); OwnConfigEntryBase[] array2 = array; foreach (OwnConfigEntryBase ownConfigEntryBase in array2) { bool isServerControlled = ownConfigEntryBase.IsServerControlled; bool isHidden = ownConfigEntryBase.IsHidden; bool defaultServerControlled = GetDefaultServerControlled(ownConfigEntryBase); ownConfigEntryBase.ClearServerValue(); ownConfigEntryBase.IsServerControlled = defaultServerControlled; ownConfigEntryBase.IsHidden = false; ownConfigEntryBase.IsPolicyStateInitialized = false; if (isServerControlled != defaultServerControlled || isHidden) { list.Add(new PolicyStateChangedEventArgs(ownConfigEntryBase, isServerControlled, defaultServerControlled, isHidden, newHidden: false, "server connection reset")); } } try { foreach (OwnConfigEntryBase item in array.Where((OwnConfigEntryBase config) => config.HasLocalBaseValue)) { ConfigFile configFile = item.BaseConfig.ConfigFile; if (!dictionary.ContainsKey(configFile)) { dictionary[configFile] = configFile.SaveOnConfigSet; configFile.SaveOnConfigSet = false; } object localBaseValue = item.LocalBaseValue; item.ClearLocalBaseValue(); configsBeingApplied.Add(item.BaseConfig); try { item.BaseConfig.BoxedValue = localBaseValue; item.StoreLastAcceptedValue(item.BaseConfig.BoxedValue); } catch (Exception arg) { if (!item.HasLocalBaseValue) { item.StoreLocalBaseValue(localBaseValue); } ConfigDefinition definition = item.BaseConfig.Definition; DebugWarning("Reset", $"Failed to restore local config {definition.Section} -> {definition.Key}; continuing. Error: {arg}"); } finally { configsBeingApplied.Remove(item.BaseConfig); } } } finally { foreach (KeyValuePair<ConfigFile, bool> item2 in dictionary) { item2.Key.SaveOnConfigSet = item2.Value; } } OwnConfigEntryBase[] array3 = array; foreach (OwnConfigEntryBase ownConfigEntryBase2 in array3) { ownConfigEntryBase2.StoreLastAcceptedValue(ownConfigEntryBase2.BaseConfig.BoxedValue); } foreach (CustomSyncedValueBase item3 in source.Where((CustomSyncedValueBase config) => config.HasLocalBaseValue)) { object localBaseValue2 = item3.LocalBaseValue; item3.ClearLocalBaseValue(); customValuesBeingApplied.Add(item3); try { item3.BoxedValue = localBaseValue2; item3.StoreLastAcceptedValue(item3.BoxedValue); } catch (Exception arg2) { if (!item3.HasLocalBaseValue) { item3.StoreLocalBaseValue(localBaseValue2); } DebugWarning("Reset", $"Failed to restore local custom value '{item3.Identifier}'; continuing. Error: {arg2}"); } finally { customValuesBeingApplied.Remove(item3); } } if (retainedSnapshot != null) { retainedSnapshot.policyTransitions.AddRange(list); return; } ServerLockedSettingChanged(); foreach (PolicyStateChangedEventArgs item4 in list) { RaisePolicyStateEvents(item4); } if (flag) { InvokeEventHandlers(this.SourceOfTruthChanged, value: true, "SourceOfTruthChanged"); } } private static OwnConfigEntryBase? GetConfigData(ConfigEntryBase config) { return config.Description.Tags?.OfType<OwnConfigEntryBase>().SingleOrDefault((OwnConfigEntryBase entry) => entry.BaseConfig == config); } [Description("Returns the synchronization wrapper attached to a registered BepInEx config entry.")] public static SyncedConfigEntry<T>? ConfigData<T>(ConfigEntry<T> config) { return GetConfigData((ConfigEntryBase)(object)config) as SyncedConfigEntry<T>; } private static T GetConfigAttribute<T>(ConfigEntryBase config) { return config.Description.Tags.OfType<T>().First(); } private static void WriteValueWithTypeToZPackage(ZPackage package, Type type, object value) { Type type2 = Nullable.GetUnderlyingType(type) ?? type; if (typeof(ISerializableParameter).IsAssignableFrom(type2)) { RequireConstructibleSerializableParameter(type2); RequireExactRuntimeType(type2, value, "ISerializableParameter"); GameReflection.SerializeParameter(value, ref package); return; } if (type2.IsEnum) { Type underlyingType = Enum.GetUnderlyingType(type2); WriteValueWithTypeToZPackage(package, underlyingType, ((IConvertible)value).ToType(underlyingType, CultureInfo.InvariantCulture)); return; } if (type2.IsPrimitive || type2 == typeof(string)) { GameReflection.PackageWrite(package, value); return; } if (type2.IsArray) { RequireVectorArray(type2); if (value is byte[] value2) { GameReflection.PackageWrite(package, value2); return; } } if (type2 != typeof(List<string>)) { Type genericCollectionType = GetGenericCollectionType(type2); if (!type2.IsArray && genericCollectionType != null) { GetCollectionImplementationType(type2, genericCollectionType); } else if (!type2.IsArray && value is ICollection) { throw new NotSupportedException("Collection type '" + type2.FullName + "' has no symmetric count/element reader in the current protocol. Declare an ICollection<T>-based type or implement ISerializableParameter."); } if (value is ICollection collection) { GameReflection.PackageWrite(package, collection.Count); Type declaredElementType = (type2.IsArray ? type2.GetElementType() : (((object)genericCollectionType != null) ? genericCollectionType.GenericTypeArguments[0] : null)); WriteCollectionElements(package, collection, declaredElementType); return; } if (genericCollectionType != null) { int value3 = (int)genericCollectionType.GetProperty("Count").GetValue(value); GameReflection.PackageWrite(package, value3); WriteCollectionElements(package, (IEnumerable)value, genericCollectionType.GenericTypeArguments[0]); return; } } if ((object)type2 != null && type2.IsValueType && !type2.IsPrimitive) { FieldInfo[] fields = type2.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); GameReflection.PackageWrite(package, fields.Length); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { GameReflection.PackageWrite(package, GetZPackageTypeString(fieldInfo.FieldType)); WriteCustomValueToPackage(package, fieldInfo.FieldType, fieldInfo.GetValue(value)); } } else { RequireExactRuntimeType(type2, value, "custom value"); int num = GameReflection.PackageGetPos(package); GameReflection.Serialize(new object[1] { value }, ref package); if (GameReflection.PackageGetPos(package) == num) { throw new NotSupportedException("Custom value type '" + type2.FullName + "' has no package serializer. Implement ISerializableParameter for this type."); } } } private static void WriteCollectionElements(ZPackage package, IEnumerable collection, Type? declaredElementType) { foreach (object item in collection) { if (item == null) { throw new NotSupportedException("Null collection elements have no encoding in the current protocol. Use ISerializableParameter for a nullable collection format."); } if (declaredElementType == null) { WriteValueWithTypeToZPackage(package, item.GetType(), item); continue; } Type type = Nullable.GetUnderlyingType(declaredElementType) ?? declaredElementType; RequireExactRuntimeType(type, item, "collection element"); RequireConstructibleSerializableParameter(type); WriteValueWithTypeToZPackage(package, type, item); } } private static void RequireExactRuntimeType(Type declaredType, object value, string context) { Type type = Nullable.GetUnderlyingType(declaredType) ?? declaredType; if (value.GetType() != type) { throw new NotSupportedException("Polymorphic " + context + " '" + value.GetType().FullName + "' cannot be encoded as declared type '" + type.FullName + "' by the current protocol. Use an exact declared element/value type or implement an explicit ISerializableParameter container format."); } } private static bool IsCompatibleZPackageTypeString(string receivedTypeName, Type expectedType) { string zPackageTypeString = GetZPackageTypeString(expectedType); return string.Equals(receivedTypeName, zPackageTypeString, StringComparison.Ordinal) || string.Equals(RemoveAssemblyVersionFromTypeIdentity(receivedTypeName), RemoveAssemblyVersionFromTypeIdentity(zPackageTypeString), StringComparison.Ordinal); } private static string RemoveAssemblyVersionFromTypeIdentity(string typeName) { return assemblyVersionTypeIdentityPattern.Replace(typeName, string.Empty); } private static void RequireConstructibleSerializableParameter(Type type) { Type type2 = Nullable.GetUnderlyingType(type) ?? type; if (!typeof(ISerializableParameter).IsAssignableFrom(type2) || (!type2.IsInterface && !type2.IsAbstract && !type2.ContainsGenericParameters && (type2.IsValueType || !(type2.GetConstructor(Type.EmptyTypes) == null)))) { return; } throw new NotSupportedException("ISerializableParameter type '" + type2.FullName + "' cannot be constructed by the matching reader. Declare a closed concrete value type or a class with a public parameterless constructor."); } private static Type? GetGenericCollectionType(Type type) { if (type.IsValueType) { return null; } return type.GetInterfaces().Concat(new Type[1] { type }).FirstOrDefault((Type candidate) => candidate.IsGenericType && candidate.GetGenericTypeDefinition() == typeof(ICollection<>)); } private static Type GetCollectionImplementationType(Type declaredType, Type collectionType) { if (!declaredType.IsInterface) { if (declaredType.IsAbstract || declaredType.GetConstructor(Type.EmptyTypes) == null) { throw new NotSupportedException("Collection type '" + declaredType.FullName + "' requires a concrete type with a public parameterless constructor or ISerializableParameter."); } return declaredType; } Type type = collectionType.GenericTypeArguments[0]; Type type2 = typeof(List<>).MakeGenericType(type); if (declaredType.IsAssignableFrom(type2)) { return type2; } Type type3 = typeof(HashSet<>).MakeGenericType(type); if (declaredType.IsAssignableFrom(type3)) { return type3; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<, >)) { Type type4 = typeof(Dictionary<, >).MakeGenericType(type.GenericTypeArguments); if (declaredType.IsAssignableFrom(type4)) { return type4; } } throw new NotSupportedException("Collection interface '" + declaredType.FullName + "' has no compatible built-in implementation. Declare a constructible concrete type or implement ISerializableParameter."); } private static void RequireVectorArray(Type type) { if (type != type.GetElementType().MakeArrayType()) { throw new NotSupportedException("Array type '" + type.FullName + "' requires shape metadata that is not present in the current protocol. Use a one-dimensional zero-based array or ISerializableParameter."); } } private static object ReadPrimitiveFromPackage(ZPackage package, Type type) { using BinaryReader binaryReader = new BinaryReader(GameReflection.PackageStream(package), Encoding.UTF8, leaveOpen: true); return Type.GetTypeCode(type) switch { TypeCode.Boolean => binaryReader.ReadBoolean(), TypeCode.Byte => binaryReader.ReadByte(), TypeCode.SByte => binaryReader.ReadSByte(), TypeCode.Char => binaryReader.ReadChar(), TypeCode.Int16 => binaryReader.ReadInt16(), TypeCode.UInt16 => binaryReader.ReadUInt16(), TypeCode.Int32 => binaryReader.ReadInt32(), TypeCode.UInt32 => binaryReader.ReadUInt32(), TypeCode.Int64 => binaryReader.ReadInt64(), TypeCode.UInt64 => binaryReader.ReadUInt64(), TypeCode.Single => binaryReader.ReadSingle(), TypeCode.Double => binaryReader.ReadDouble(), _ => throw new NotSupportedException("Primitive custom value type '" + type.FullName + "' has no package encoding."), }; } private static object ReadValueWithTypeFromZPackage(ZPackage package, Type type) { Type type2 = Nullable.GetUnderlyingType(type) ?? type; if (typeof(ISerializableParameter).IsAssignableFrom(type2)) { RequireConstructibleSerializableParameter(type2); object obj = Activator.CreateInstance(type2) ?? throw new MissingMethodException("Cannot create " + type2.FullName + " for ISerializableParameter deserialization"); GameReflection.DeserializeParameter(obj, ref package); return obj; } if (type2.IsEnum) { object value = ReadValueWithTypeFromZPackage(package, Enum.GetUnderlyingType(type2)); return Enum.ToObject(type2, value); } if (type2.IsPrimitive) { return ReadPrimitiveFromPackage(package, type2); } if (type2 == typeof(string)) { return GameReflection.PackageReadString(package); } if (type2.IsArray) { RequireVectorArray(type2); if (type2 == typeof(byte[])) { return GameReflection.PackageReadByteArray(package, 20971520); } int num = GameReflection.PackageReadInt(package); Type elementType = type2.GetElementType(); Array array = Array.CreateInstance(elementType, num); for (int i = 0; i < num; i++) { array.SetValue(ReadValueWithTypeFromZPackage(package, elementType), i); } return array; } if ((object)type2 != null && type2.IsValueType && !type2.IsPrimitive) { FieldInfo[] fields = type2.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); int num2 = GameReflection.PackageReadInt(package); if (num2 != fields.Length) { throw new InvalidDeserializationTypeException { received = $"(field count: {num2})", expected = $"(field count: {fields.Length})" }; } object uninitializedObject = FormatterServices.GetUninitializedObject(type2); FieldInfo[] array2 = fields; foreach (FieldInfo fieldInfo in array2) { string text = GameReflection.PackageReadString(package); if (!IsCompatibleZPackageTypeString(text, fieldInfo.FieldType)) { throw new InvalidDeserializationTypeException { received = text, expected = GetZPackageTypeString(fieldInfo.FieldType), field = fieldInfo.Name }; } fieldInfo.SetValue(uninitializedObject, ReadCustomValueFromPackage(package, fieldInfo.FieldType)); } return uninitializedObject; } if (type2.IsGenericType && type2.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { int num3 = GameReflection.PackageReadInt(package); IDictionary dictionary = (IDictionary)Activator.CreateInstance(type2); Type type3 = typeof(KeyValuePair<, >).MakeGenericType(type2.GenericTypeArguments); FieldInfo field = type3.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type3.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); for (int k = 0; k < num3; k++) { object obj2 = ReadValueWithTypeFromZPackage(package, type3); dictionary.Add(field.GetValue(obj2), field2.GetValue(obj2)); } return dictionary; } if (type2 != typeof(List<string>)) { Type genericCollectionType = GetGenericCollectionType(type2); if ((object)genericCollectionType != null) { Type collectionImplementationType = GetCollectionImplementationType(type2, genericCollectionType); int num4 = GameReflection.PackageReadInt(package); object obj3 = Activator.CreateInstance(collectionImplementationType); Type type4 = genericCollectionType.GenericTypeArguments[0]; MethodInfo method = genericCollectionType.GetMethod("Add"); for (int l = 0; l < num4; l++) { method.Invoke(obj3, new object[1] { ReadValueWithTypeFromZPackage(package, type4) }); } return obj3; } } ParameterInfo parameterInfo = (ParameterInfo)FormatterServices.GetUninitializedObject(typeof(ParameterInfo)); AccessTools.DeclaredField(typeof(ParameterInfo), "ClassImpl").SetValue(parameterInfo, type2); List<object> data = new List<object>(); GameReflection.Deserialize(new ParameterInfo[2] { null, parameterInfo }, package, ref data); if (data.Count != 1) { throw new NotSupportedException("Custom value type '" + type2.FullName + "' has no package deserializer. Implement ISerializableParameter for this type."); } return data[0]; } private static void EnsureDebugSupportInitialized() { lock (debugSupportLock) { if (!debugSupportInitialized) { debugSupportInitialized = true; LoadDebugConfig(createIfMissing: true, quiet: true); StartDebugConfigWatcher(); } } } private static void StartDebugConfigWatcher() { try { EnsureConfigDirectory(); FileSystemWatcher watcher = new FileSystemWatcher(ConfigDirectoryPath, "ConditionalConfigSync.Debug.cfg") { NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime) }; watcher.Changed += delegate { ScheduleDebugConfigReload(watcher); }; watcher.Created += delegate { ScheduleDebugConfigReload(watcher); }; watcher.Renamed += delegate { ScheduleDebugConfigReload(watcher); }; watcher.Deleted += delegate { ScheduleDebugConfigReload(watcher); }; debugConfigWatcher = watcher; watcher.EnableRaisingEvents = true; } catch (Exception ex) { LogSource.LogWarning((object)("[DebugConfig] Could not start watcher for ConditionalConfigSync.Debug.cfg: " + ex.Message)); } } private static void ScheduleDebugConfigReload(FileSystemWatcher watcher) { lock (debugSupportLock) { if (!debugSupportInitialized || watcher != debugConfigWatcher || debugConfigReloadScheduled) { return; } debugConfigReloadScheduled = true; } ThreadPool.QueueUserWorkItem(delegate { Thread.Sleep(500); lock (debugSupportLock) { if (!debugSupportInitialized || watcher != debugConfigWatcher) { return; } try { LoadDebugConfig(createIfMissing: false, quiet: false); } finally { debugConfigReloadScheduled = false; } } }); } private static void LoadDebugConfig(bool createIfMissing, bool quiet) { try { EnsureConfigDirectory(); if (!File.Exists(DebugConfigPath)) { if (createIfMissing) { File.WriteAllText(DebugConfigPath, "[Debug]\n# Local debug logging for ConditionalConfigSync. This file is not synchronized.\nEnabled = false\nLevel = Basic\nFilter = \n"); return; } debugConfigEnabled = false; debugConfigLevel = ConditionalConfigSyncDebugLevel.Basic; debugConfigFilter = ""; if (!quiet && DebugLoggingEnabled) { LogSource.LogInfo((object)"[DebugConfig] ConditionalConfigSync.Debug.cfg was deleted; file-based debug settings were reset."); } return; } Dictionary<string, string> dictionary = ReadSimpleDebugConfig(DebugConfigPath); bool flag = ReadBool(dictionary, "Enabled", fallback: false); string value; ConditionalConfigSyncDebugLevel conditionalConfigSyncDebugLevel = ReadDebugLevel(dictionary.TryGetValue("Level", out value) ? value : null, ConditionalConfigSyncDebugLevel.Basic); string value2; string text = (dictionary.TryGetValue("Filter", out value2) ? value2.Trim() : ""); debugConfigEnabled = flag; debugConfigLevel = conditionalConfigSyncDebugLevel; debugConfigFilter = text; if (!quiet && (flag || DebugLoggingEnabled)) { LogSource.LogInfo((object)string.Format("[DebugConfig] Reloaded {0}: enabled={1}, level={2}, filter='{3}'", "ConditionalConfigSync.Debug.cfg", flag, conditionalConfigSyncDebugLevel, text)); } } catch (Exception ex) { LogSource.LogWarning((object)("[DebugConfig] Failed to read ConditionalConfigSync.Debug.cfg: " + ex.Message)); } } private static Dictionary<string, string> ReadSimpleDebugConfig(string path) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); string text = ""; string[] array = ReadAllLinesStable(path); foreach (string text2 in array) { string text3 = text2.Trim(); if (text3.Length == 0 || text3.StartsWith("#") || text3.StartsWith(";")) { continue; } if (text3.StartsWith("[") && text3.EndsWith("]")) { text = text3.Substring(1, text3.Length - 2).Trim(); } else if (text.Equals("Debug", StringComparison.OrdinalIgnoreCase)) { int num = text3.IndexOf('='); if (num >= 0) { dictionary[text3.Substring(0, num).Trim()] = text3.Substring(num + 1).Trim(); } } } return dictionary; } private static bool ReadBool(Dictionary<string, string> values, string key, bool fallback) { if (!values.TryGetValue(key, out string value)) { return fallback; } if (bool.TryParse(value, out var result)) { return result; } if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { return result2 != 0; } return fallback; } private static ConditionalConfigSyncDebugLevel ReadDebugLevel(string? raw, ConditionalConfigSyncDebugLevel fallback) { ConditionalConfigSyncDebugLevel result; return (Enum.TryParse<ConditionalConfigSyncDebugLevel>(raw, ignoreCase: true, out result) && Enum.IsDefined(typeof(ConditionalConfigSyncDebugLevel), result)) ? result : fallback; } private bool ShouldDebugLog(ConditionalConfigSyncDebugLevel level) { if (!IsDebugActive || level > EffectiveDebugLevel) { return false; } string effectiveDebugFilter = EffectiveDebugFilter; if (string.IsNullOrWhiteSpace(effectiveDebugFilter)) { return true; } string text = DisplayName ?? Name; return text.IndexOf(effectiveDebugFilter, StringComparison.OrdinalIgnoreCase) >= 0 || Name.IndexOf(effectiveDebugFilter, StringComparison.OrdinalIgnoreCase) >= 0; } private void DebugLog(ConditionalConfigSyncDebugLevel level, string area, string message) { if (ShouldDebugLog(level)) { LogSource.LogInfo((object)("[" + GetDebugModName() + "][" + GetDebugSide() + "][" + area + "] " + message)); } } private void InfoLog(string message) { LogSource.LogInfo((object)("[" + GetDebugModName() + "][" + GetDebugSide() + "] " + message)); } private void DebugWarning(string area, string message) { LogSource.LogWarning((object)("[" + GetDebugModName() + "][" + GetDebugSide() + "][" + area + "] " + message)); } private static double ElapsedMilliseconds(long startTimestamp) { return (double)(Stopwatch.GetTimestamp() - startTimestamp) * 1000.0 / (double)Stopwatch.Frequency; } private static string FormatByteCount(int bytes) { if (bytes >= 1048576) { return string.Format("{0} MiB ({1} bytes)", ((double)bytes / 1048576.0).ToString("F2", CultureInfo.InvariantCulture), bytes); } if (bytes >= 1024) { return string.Format("{0} KiB ({1} bytes)", ((double)bytes / 1024.0).ToString("F1", CultureInfo.InvariantCulture), bytes); } return $"{bytes} bytes"; } private static string FormatMilliseconds(double milliseconds) { return milliseconds.ToString("F2", CultureInfo.InvariantCulture) + " ms"; } private static string FormatCompressionStats(int rawSize, int compressedSize) { if (rawSize <= 0) { return "raw=" + FormatByteCount(rawSize) + ", compressed=" + FormatByteCount(compressedSize); } double num = (double)compressedSize * 100.0 / (double)rawSize; double num2 = 100.0 - num; return "raw=" + FormatByteCount(rawSize) + ", compressed=" + FormatByteCount(compressedSize) + ", ratio=" + num.ToString("F1", CultureInfo.InvariantCulture) + "%, saved=" + num2.ToString("F1", CultureInfo.InvariantCulture) + "%"; } private string GetDebugModName() { return (!string.IsNullOrWhiteSpace(DisplayName)) ? DisplayName : Name; } private static string GetDebugSide() { if (!GameReflection.HasZNet) { return "NoZNet"; } return GameReflection.IsServer() ? "Server" : "Client"; } private void InvokeEventHandlers(Action? handlers, string eventName) { if (handlers == null) { return; } Delegate[] invocationList = handlers.GetInvocationList(); foreach (Delegate obj in invocationList) { try { ((Action)obj)(); } catch (Exception arg) { DebugWarning("Event", $"Subscriber of {eventName} failed; continuing. Error: {arg}"); } } } private void InvokeEventHandlers<T>(Action<T>? handlers, T value, string eventName) { if (handlers == null) { return; } Delegate[] invocationList = handlers.GetInvocationList(); foreach (Delegate obj in invocationList) { try { ((Action<T>)obj)(value); } catch (Exception arg) { DebugWarning("Event", $"Subscriber of {eventName} failed; continuing. Error: {arg}"); } } } private void InvokeEventHandlers<T1, T2>(Action<T1, T2>? handlers, T1 value1, T2 value2, string eventName) { if (handlers == null) { return; } Delegate[] invocationList = handlers.GetInvocationList(); foreach (Delegate obj in invocationList) { try { ((Action<T1, T2>)obj)(value1, value2); } catch (Exception arg) { DebugWarning("Event", $"Subscriber of {eventName} failed; continuing. Error: {arg}"); } } } private void InvokeEventHandlers<TEventArgs>(EventHandler<TEventArgs>? handlers, TEventArgs args, string eventName) where TEventArgs : EventArgs { if (handlers == null) { return; } Delegate[] invocationList = handlers.GetInvocationList(); foreach (Delegate obj in invocationList) { try { ((EventHandler<TEventArgs>)obj)(this, args); } catch (Exception arg) { DebugWarning("Event", $"Subscriber of {eventName} failed; continuing. Error: {arg}"); } } } private void RaiseInitialSyncCompletedIfNeeded() { if (!InitialSyncDone) { long num = transportGeneration; InitialSyncDone = true; ServerLockedSettingChanged(); if (sessionActive && num == transportGeneration && InitialSyncDone) { InvokeEventHandlers(this.InitialSyncCompleted, "InitialSyncCompleted"); ScheduleLateRegistrationSync(); } } } private void RaisePolicyStateEvents(OwnConfigEntryBase config, bool oldServerControlled, bool newServerControlled, bool oldHidden, bool newHidden, string source) { if (oldServerControlled != newServerControlled || oldHidden != newHidden) { RaisePolicyStateEvents(new PolicyStateChangedEventArgs(config, oldServerControlled, newServerControlled, oldHidden, newHidden, source)); } } private void RaisePolicyStateEvents(PolicyStateChangedEventArgs args) { InvokeEventHandlers(this.PolicyStateChanged, args, "PolicyStateChanged"); if (args.OldServerControlled != args.NewServerControlled) { InvokeEventHandlers(this.ServerControlledChanged, args.Config, args.NewServerControlled, "ServerControlledChanged"); } if (args.OldHidden != args.NewHidden) { InvokeEventHandlers(this.HiddenStateChanged, args.Config, args.NewHidden, "HiddenStateChanged"); } } private void RaiseLockStateChangedIfNeeded() { bool isLocked = IsLocked; if (lastNotifiedLockState != isLocked) { lastNotifiedLockState = isLocked; InvokeEventHandlers(this.LockStateChanged, isLocked, "LockStateChanged"); } } private void RejectSync(string reason, long? senderUid, bool incoming, Exception? exception = null) { DebugWarning("Reject", (exception == null) ? reason : $"{reason}{Environment.NewLine}{exception}"); InvokeEventHandlers(this.SyncRejected, new SyncRejectedEventArgs(reason, senderUid, incoming, exception), "SyncRejected"); } private static void RegisterDebugConsoleCommands() { if (debugCommandsRegistered) { return; } try { MethodInfo handler = AccessTools.DeclaredMethod(typeof(ConditionalConfigSync), "HandleDebugConsoleCommand", (Type[])null, (Type[])null) ?? throw new MissingMethodException(typeof(ConditionalConfigSync).FullName, "HandleDebugConsoleCommand"); MethodInfo handler2 = AccessTools.DeclaredMethod(typeof(ConditionalConfigSync), "HandlePolicyConsoleCommand", (Type[])null, (Type[])null) ?? throw new MissingMethodException(typeof(ConditionalConfigSync).FullName, "HandlePolicyConsoleCommand"); GameReflection.RegisterConsoleCommand("conditionalconfigsync_debug", "ConditionalConfigSync debug logging: status/on/off/level/filter/clearfilter", handler); GameReflection.RegisterConsoleCommand("conditionalconfigsync_debug_server", "ConditionalConfigSync server debug logging: status/on/off/level/filter/clearfilter", handler, isCheat: false, isNetwork: true, onlyServer: true, isSecret: false, allowInDevBuild: false, remoteCommand: true, onlyAdmin: true); RegisterPolicyConsoleCommand("conditionalconfigsync_status", "Show registered mods, config modes, and effective policy counts", handler2); RegisterPolicyConsoleCommand("conditionalconfigsync_policy_reload", "Reload and apply ConditionalConfigSync policy files", handler2); RegisterPolicyConsoleCommand("conditionalconfigsync_policy_validate", "Validate policy syntax, identifiers, and ignored rules", handler2); RegisterPolicyConsoleCommand("conditionalconfigsync_policy_dump", "Write a copy-ready policy identifier file", handler2); debugCommandsRegistered = true; } catch (Exception ex) { LogSource.LogWarning((object)("[Console] Could not register ConditionalConfigSync commands: " + ex.Message)); } } private static void RegisterPolicyConsoleCommand(string command, string description, MethodInfo handler) { GameReflection.RegisterConsoleCommand(command, description, handler, isCheat: false, isNetwork: true, onlyServer: true, isSecret: false, allowInDevBuild: false, remoteCommand: true, onlyAdmin: true); } private static object HandleDebugConsoleCommand(ConsoleEventArgs args) { EnsureDebugSupportInitialized(); bool flag = GameReflection.ConsoleArgsLength(args) <= 1; bool? flag2 = null; ConditionalConfigSyncDebugLevel? conditionalConfigSyncDebugLevel = null; string text = null; for (int i = 1; i < GameReflection.ConsoleArgsLength(args); i++) { string text2 = GameReflection.ConsoleArg(args, i).Trim(); switch (text2.ToLowerInvariant()) { case "status": flag = true; break; case "on": case "enable": case "enabled": flag2 = true; flag = false; break; case "off": case "disable": case "disabled": flag2 = false; flag = false; break; case "basic": case "verbose": case "trace": conditionalConfigSyncDebugLevel = ReadDebugLevel(text2, DebugLoggingLevel); flag = false; break; case "level": if (i + 1 < GameReflection.ConsoleArgsLength(args)) { conditionalConfigSyncDebugLevel = ReadDebugLevel(GameReflection.ConsoleArg(args, ++i), DebugLoggingLevel); flag = false; } break; case "filter": if (i + 1 < GameReflection.ConsoleArgsLength(args)) { text = GameReflection.ConsoleArg(args, ++i); flag = false; } break; case "clearfilter": case "nofilter": text = ""; flag = false; break; default: return "Unknown argument '" + text2 + "'"; } } if (flag) { WriteDebugStatus(args); return true; } DebugLoggingEnabled = flag2 ?? DebugLoggingEnabled; DebugLoggingLevel = conditionalConfigSyncDebugLevel ?? DebugLoggingLevel; DebugLoggingFilter = text ?? DebugLoggingFilter; AddConsoleLine(args, $"ConditionalConfigSync debug: enabled={DebugLoggingEnabled}, level={DebugLoggingLevel}, filter='{DebugLoggingFilter}'"); return true; } private static void WriteDebugStatus(ConsoleEventArgs args) { AddConsoleLine(args, $"ConditionalConfigSync debug: enabled={DebugLoggingEnabled}, configEnabled={debugConfigEnabled}, level={EffectiveDebugLevel}, filter='{EffectiveDebugFilter}'"); } private static void AddConsoleLine(ConsoleEventArgs args, string text) { GameReflection.ConsoleAddString(args, text); LogSource.LogInfo((object)("[Console] " + text)); } internal static void VersionDebugLog(string area, string modName, string message) { EnsureDebugSupportInitialized(); if (IsDebugActive && ConditionalConfigSyncDebugLevel.Basic <= EffectiveDebugLevel) { string effectiveDebugFilter = EffectiveDebugFilter; if (string.IsNullOrWhiteSpace(effectiveDebugFilter) || modName.IndexOf(effectiveDebugFilter, StringComparison.OrdinalIgnoreCase) >= 0) { LogSource.LogInfo((object)("[" + modName + "][" + GetDebugSide() + "][" + area + "] " + message)); } } } internal static void VersionInfoLog(string area, string modName, string message) { LogSource.LogInfo((object)("[" + modName + "][" + GetDebugSide() + "][" + area + "] " + message)); } internal static void VersionWarningLog(string area, string modName, string message) { LogSource.LogWarning((object)("[" + modName + "][" + GetDebugSide() + "][" + area + "] " + message)); } internal static void VersionErrorLog(string area, string modName, string message) { LogSource.LogError((object)("[" + modName + "][" + GetDebugSide() + "][" + area + "] " + message)); } private void ApplyParsedConfigs(ParsedConfigs configs, bool receivedFromServer) { DebugLog(ConditionalConfigSyncDebugLevel.Verbose, "Apply", $"Applying configs={configs.configValues.Count}, custom={configs.customValues.Count}, states={configs.configStates.Count}"); Dictionary<ConfigFile, bool> saveOnConfigSet = new Dictionary<ConfigFile, bool>(); List<PolicyStateChangedEventArgs> policyTransitions = configs.policyTransitions; long generation = transportGeneration; if (receivedFromServer) { lateRegisteredConfigs.ExceptWith(configs.configValues.Keys); lateRegisteredConfigs.ExceptWith(configs.configStates.Keys); lateRegisteredCustomValues.ExceptWith(configs.customValues.Keys); } try { if (receivedFromServer) { foreach (KeyValuePair<OwnConfigEntryBase, ReceivedConfigState> configState in configs.configStates) { bool isServerControlled = configState.Key.IsServerControlled; bool isHidden = configState.Key.IsHidden; configState.Key.IsServerControlled = configState.Value.ServerControlled; configState.Key.IsHidden = configState.Value.Hidden; configState.Key.IsPolicyStateInitialized = true; if (isServerControlled != configState.Value.ServerControlled || isHidden != configState.Value.Hidden) { policyTransitions.Add(new PolicyStateChangedEventArgs(configState.Key, isServerControlled, configState.Value.ServerControlled, isHidden, configState.Value.Hidden, "server package")); } } } foreach (KeyValuePair<OwnConfigEntryBase, object> configValue in configs.configValues) { if (!IsCurrentApplication()) { return; } if (receivedFromServer) { configValue.Key.StoreServerValue(configValue.Value); if (!configValue.Key.IsServerControlled) { RestoreLocalValueIfNeeded(configValue.Key); continue; } if (!configValue.Key.HasLocalBaseValue) { configValue.Key.StoreLocalBaseValue(configValue.Key.BaseConfig.BoxedValue); } } SetConfigValue(configValue.Key, configValue.Value); } if (receivedFromServer) { foreach (KeyValuePair<OwnConfigEntryBase, ReceivedConfigState> configState2 in configs.configStates) { if (!IsCurrentApplication()) { return; } if (!configs.configValues.ContainsKey(configState2.Key)) { if (configState2.Key.IsServerControlled) { ApplyServerValueIfAvailable(configState2.Key); } else { RestoreLocalValueIfNeeded(configState2.Key); } } } } } finally { foreach (KeyValuePair<ConfigFile, bool> item in saveOnConfigSet) { item.Key.SaveOnConfigSet = item.Value; if (IsCurrentApplication()) { try { item.Key.Save(); } catch (Exception arg) { DebugWarning("Apply", $"Failed to save config file '{item.Key.ConfigFilePath}' after applying synchronized values: {arg}"); } } } } foreach (KeyValuePair<CustomSyncedValueBase, object> item2 in from entry in configs.customValues orderby entry.Key.Priority descending, entry.Key.RegistrationIndex select entry) { if (!IsCurrentApplication()) { return; } if (!isServer && !item2.Key.HasLocalBaseValue) { item2.Key.StoreLocalBaseValue(item2.Key.BoxedValue); } customValuesBeingApplied.Add(item2.Key); try { item2.Key.BoxedValue = item2.Value; if (IsCurrentApplication()) { item2.Key.StoreLastAcceptedValue(item2.Key.BoxedValue); } } catch (Exception arg2) { DebugWarning("Apply", $"Failed to apply custom value '{item2.Key.Identifier}'; continuing with the remaining entries. Error: {arg2}"); } finally { customValuesBeingApplied.Remove(item2.Key); } } if (!IsCurrentApplication()) { return; } if (receivedFromServer && configs.capabilities.HasValue) { remotePolicyChangeSupported = (configs.capabilities.Value & 1) != 0; } ConditionalConfigSync[] array = (receivedFromServer ? configSyncs.ToArray() : new ConditionalConfigSync[1] { this }); ConditionalConfigSync[] array2 = array; foreach (ConditionalConfigSync conditionalConfigSync in array2) { if (!IsCurrentApplication()) { return; } conditionalConfigSync.UpdateConfigMetadata(); } ConditionalConfigSync[] array3 = array; foreach (ConditionalConfigSync conditionalConfigSync2 in array3) { if (!IsCurrentApplication()) { return; } if (conditionalConfigSync2 == this || conditionalConfigSync2.CanPublishConfigStateNotifications) { conditionalConfigSync2.RaiseLockStateChangedIfNeeded(); } } foreach (PolicyStateChangedEventArgs item3 in policyTransitions) { if (!IsCurrentApplication()) { break; } RaisePolicyStateEvents(item3); } void ApplyServerValueIfAvailable(OwnConfigEntryBase config) { if (config.HasServerValue) { if (!config.HasLocalBaseValue) { config.StoreLocalBaseValue(config.BaseConfig.BoxedValue); } SetConfigValue(config, config.ServerValue); } } void DisableSaveOnConfigSet(ConfigFile configFile) { if (!saveOnConfigSet.ContainsKey(configFile)) { saveOnConfigSet[configFile] = configFile.SaveOnConfigSet; configFile.SaveOnConfigSet = false; } } bool IsCurrentApplication() { return sessionActive && generation == transportGeneration; } void RestoreLocalValueIfNeeded(OwnConfigEntryBase config) { if (config.HasLocalBaseValue && SetConfigValue(config, config.LocalBaseValue)) { config.ClearLocalBaseValue(); } } bool SetConfigValue(OwnConfigEntryBase config, object? value) { if (IsCurrentApplication()) { ConfigFile configFile = config.BaseConfig.ConfigFile; DisableSaveOnConfigSet(configFile); configsBeingApplied.Add(config.BaseConfig); try { config.BaseConfig.BoxedValue = value; if (!IsCurrentApplication()) { return false; } config.StoreLastAcceptedValue(config.BaseConfig.BoxedValue); return true; } catch (Exception arg3) { ConfigDefinition definition = config.BaseConfig.Definition; DebugWarning("Apply", $"Failed to apply config {definition.Section} -> {definition.Key}; continuing with the remaining entries. Error: {arg3}"); return false; } finally { configsBeingApplied.Remove(config.BaseConfig); } } return false; } } private static string GetSingleEntryReceiveDetails(ParsedConfigs configs) { List<string> list = new List<string>(); if (configs.configValues.Count == 1) { ConfigDefinition definition = configs.configValues.Keys.First().BaseConfig.Definition; list.Add("config=" + definition.Section + " -> " + definition.Key); } if (configs.customValues.Count == 1) { list.Add("custom=" + configs.customValues.Keys.First().Identifier); } return (list.Count == 0) ? "" : (" (" + string.Join(", ", list) + ")"); } private ParsedConfigs ReadConfigsFromPackage(ZPackage package, bool receivedFromServer, bool strictClientUpdate = false) { ParsedConfigs parsedConfigs = new ParsedConfigs(); Dictionary<string, OwnConfigEntryBase> dictionary = allConfigs.ToDictionary((OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "\n" + c.BaseConfig.Definition.Key, (OwnConfigEntryBase c) => c); Dictionary<string, CustomSyncedValueBase> dictionary2 = allCustomValues.ToDictionary((CustomSyncedValueBase c) => c.Identifier, (CustomSyncedValueBase c) => c); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); int num = (parsedConfigs.entryCount = GameReflection.PackageReadInt(package)); if (num < 0 || num > 8192) { throw new InvalidDataException($"Invalid config entry count {num}"); } for (int num2 = 0; num2 < num; num2++) { PackageEntryKind packageEntryKind = (PackageEntryKind)GameReflection.PackageReadByte(package); byte[] array = GameReflection.PackageReadByteArray(package, 20971520); if (array.Length > 20971520) { if (strictClientUpdate) { parsedConfigs.Reject($"entry {num2 + 1} exceeds the {20971520}-byte entry limit"); } else { DebugWarning("Read", $"Skipping too large entry payload ({array.Length})"); } continue; } ZPackage package2 = GameReflection.NewPackage(array); switch (packageEntryKind) { case PackageEntryKind.Config: { string text = GameReflection.PackageReadString(package2); string text2 = GameReflection.PackageReadString(package2); string text3 = GameReflection.PackageReadString(package2); string item = "config\n" + text + "\n" + text2; if (!hashSet.Add(item)) { if (strictClientUpdate) { parsedConfigs.Reject("duplicate config entry " + text + " -> " + text2); } break; } if (dictionary.TryGetValue(text + "\n" + text2, out var value)) { try { parsedConfigs.configValues[value] = TomlTypeConverter.ConvertToValue(text3, value.BaseConfig.SettingType); } catch (Exception ex) { if (strictClientUpdate) { parsedConfigs.Reject("config " + text + " -> " + text2 + " could not be parsed: " + ex.Message); } else { DebugWarning("Read", $"Config value of setting \"{value.BaseConfig.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}"); } } } else if (strictClientUpdate) { parsedConfigs.Reject("unknown config entry " + text + " -> " + text2); } else { DebugWarning("Read", "Received unknown config entry " + text + "/" + text2 + ". This may happen if client and server versions of the mod do not match."); } if (strictClientUpdate && GameReflection.PackageGetPos(package2) != GameReflection.PackageSize(package2)) { parsedConfigs.Reject("config entry " + text + " -> " + text2 + " contains trailing data"); } break; } case PackageEntryKind.ConfigState: { string text4 = GameReflection.PackageReadString(package2); string text5 = GameReflection.PackageReadString(package2); bool serverControlled = GameReflection.PackageReadBool(package2); bool hidden = GameReflection.PackageReadBool(package2); string item2 = "state\n" + text4 + "\n" + text5; if (!hashSet.Add(item2)) { if (strictClientUpdate) { parsedConfigs.Reject("duplicate config state " + text4 + " -> " + text5); } break; } if (dictionary.TryGetValue(text4 + "\n" + text5, out var value2)) { if (strictClientUpdate) { p
ConditionalConfigSync.Plugin.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Conditional Config Sync Plugin")] [assembly: AssemblyDescription("BepInEx bootstrap for the Conditional Config Sync package.")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("shudnal")] [assembly: AssemblyProduct("Conditional Config Sync")] [assembly: AssemblyCopyright("Copyright © shudnal 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("815dc453-badf-4351-b8aa-92321836fcb3")] [assembly: AssemblyFileVersion("1.0.10")] [assembly: AssemblyInformationalVersion("1.0.10")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/shudnal/ConditionalConfigSync")] [assembly: AssemblyMetadata("License", "Unlicense")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.10.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ConditionalConfigSync { [BepInPlugin("_shudnal.ConditionalConfigSync", "Conditional Config Sync", "1.0.10")] [Description("Standalone BepInEx bootstrap that initializes ConditionalConfigSync.dll.")] public sealed class ConditionalConfigSyncPlugin : BaseUnityPlugin { [Description("BepInEx hard-dependency identifier used by mods that consume ConditionalConfigSync.")] public const string PluginGuid = "_shudnal.ConditionalConfigSync"; [Description("Display name of the standalone BepInEx plugin.")] public const string PluginName = "Conditional Config Sync"; [Description("Package version sourced from PluginInfo.PluginVersion.")] public const string PluginVersion = "1.0.10"; private const string PluginAssemblyName = "ConditionalConfigSync.Plugin"; private const string CoreAssemblyName = "ConditionalConfigSync"; private const string ActiveBootstrapKey = "_shudnal.ConditionalConfigSync.ActiveBootstrapInstance"; private bool ownsBootstrap; private void Awake() { string text = typeof(ConditionalConfigSyncPlugin).Assembly.GetName().Name ?? string.Empty; if (!string.Equals(text, "ConditionalConfigSync.Plugin", StringComparison.Ordinal)) { ((BaseUnityPlugin)this).Logger.LogFatal((object)("Embedded ConditionalConfigSync bootstrap detected inside assembly '" + text + "'. The bootstrap plugin must only run from ConditionalConfigSync.Plugin.dll.")); ((Behaviour)this).enabled = false; return; } string text2 = typeof(ConditionalConfigSync).Assembly.GetName().Name ?? string.Empty; if (!string.Equals(text2, "ConditionalConfigSync", StringComparison.Ordinal)) { ((BaseUnityPlugin)this).Logger.LogFatal((object)("ConditionalConfigSync core was resolved from unsupported assembly '" + text2 + "'. Remove embedded copies and install the standalone ConditionalConfigSync mod.")); ((Behaviour)this).enabled = false; return; } if (!TryClaimBootstrap()) { ((Behaviour)this).enabled = false; return; } try { ConditionalConfigSync.InitializeRuntime(); } catch { ReleaseBootstrap(); ((Behaviour)this).enabled = false; throw; } } private bool TryClaimBootstrap() { AppDomain currentDomain = AppDomain.CurrentDomain; lock (currentDomain) { object data = currentDomain.GetData("_shudnal.ConditionalConfigSync.ActiveBootstrapInstance"); if (data != null && data != this) { Assembly assembly = data.GetType().Assembly; Assembly assembly2 = typeof(ConditionalConfigSyncPlugin).Assembly; ((BaseUnityPlugin)this).Logger.LogFatal((object)("A second ConditionalConfigSync bootstrap attempted to initialize while another bootstrap is already active. Active bootstrap: " + DescribeAssembly(assembly) + ". Attempted bootstrap: " + DescribeAssembly(assembly2) + ".")); return false; } currentDomain.SetData("_shudnal.ConditionalConfigSync.ActiveBootstrapInstance", this); ownsBootstrap = true; return true; } } private static string DescribeAssembly(Assembly assembly) { string text; try { text = (assembly.IsDynamic ? "<dynamic>" : assembly.Location); } catch { text = "<unknown location>"; } return "'" + assembly.FullName + "' at '" + text + "'"; } private void ReleaseBootstrap() { if (!ownsBootstrap) { return; } AppDomain currentDomain = AppDomain.CurrentDomain; lock (currentDomain) { if (currentDomain.GetData("_shudnal.ConditionalConfigSync.ActiveBootstrapInstance") == this) { currentDomain.SetData("_shudnal.ConditionalConfigSync.ActiveBootstrapInstance", null); } } ownsBootstrap = false; } private void OnDestroy() { if (!ownsBootstrap) { return; } try { ConditionalConfigSync.ShutdownRuntime(); } finally { ReleaseBootstrap(); } } } public static class SoftDependencyBridge { private static readonly MethodInfo AddConfigEntryDefinition = typeof(ConditionalConfigSync).GetMethods(BindingFlags.Instance | BindingFlags.Public).Single(delegate(MethodInfo method) { if (method.Name != "AddConfigEntry" || !method.IsGenericMethodDefinition) { return false; } ParameterInfo[] parameters = method.GetParameters(); return parameters.Length == 3 && parameters[0].ParameterType.IsGenericType && parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(ConfigEntry<>) && parameters[1].ParameterType == typeof(ConfigSyncMode) && parameters[2].ParameterType == typeof(bool); }); private static readonly MethodInfo AddLockingConfigEntryDefinition = typeof(ConditionalConfigSync).GetMethods(BindingFlags.Instance | BindingFlags.Public).Single(delegate(MethodInfo method) { if (method.Name != "AddLockingConfigEntry" || !method.IsGenericMethodDefinition) { return false; } ParameterInfo[] parameters = method.GetParameters(); return parameters.Length == 1 && parameters[0].ParameterType.IsGenericType && parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(ConfigEntry<>); }); private static readonly object MethodCacheLock = new object(); private static readonly Dictionary<Type, MethodInfo> AddConfigEntryMethods = new Dictionary<Type, MethodInfo>(); private static readonly Dictionary<Type, MethodInfo> AddLockingConfigEntryMethods = new Dictionary<Type, MethodInfo>(); public static int ApiVersion => 1; public static bool IsRuntimeReady { get { try { ConditionalConfigSync.EnsureRuntimeReady(); return true; } catch { return false; } } } public static object CreateConfigSync(string name, string displayName, string currentVersion, string minimumRequiredVersion, bool modRequired, string modRequirementMode, bool allowClientConfigUpdatesWhenUnlocked) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("A stable consumer GUID is required.", "name"); } ConditionalConfigSync.EnsureRuntimeReady(); if (!Enum.TryParse<ModRequirementMode>(modRequirementMode, ignoreCase: false, out ModRequirementMode result) || !Enum.IsDefined(typeof(ModRequirementMode), result)) { throw new ArgumentOutOfRangeException("modRequirementMode", modRequirementMode, "Unknown Conditional Config Sync mod requirement mode."); } return (object)new ConfigSync(name) { DisplayName = (string.IsNullOrWhiteSpace(displayName) ? name : displayName), CurrentVersion = currentVersion, MinimumRequiredVersion = minimumRequiredVersion, ModRequired = modRequired, ModRequirementMode = result, AllowClientConfigUpdatesWhenUnlocked = allowClientConfigUpdatesWhenUnlocked }; } public static void RegisterConfigEntry(object configSyncInstance, ConfigEntryBase configEntry, string syncMode, bool serverControlledByDefault) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) ConditionalConfigSync.EnsureRuntimeReady(); ConditionalConfigSync val = (ConditionalConfigSync)((configSyncInstance is ConditionalConfigSync) ? configSyncInstance : null); if (val == null) { throw new ArgumentException("The supplied context was not created by Conditional Config Sync.", "configSyncInstance"); } if (configEntry == null) { throw new ArgumentNullException("configEntry"); } if (!Enum.TryParse<ConfigSyncMode>(syncMode, ignoreCase: false, out ConfigSyncMode result) || !Enum.IsDefined(typeof(ConfigSyncMode), result)) { throw new ArgumentOutOfRangeException("syncMode", syncMode, "Unknown Conditional Config Sync config mode."); } MethodInfo closedMethod = GetClosedMethod(AddConfigEntryDefinition, AddConfigEntryMethods, configEntry.SettingType); Invoke(closedMethod, val, new object[3] { configEntry, result, serverControlledByDefault }); } public static void RegisterLockingConfigEntry(object configSyncInstance, ConfigEntryBase configEntry) { ConditionalConfigSync.EnsureRuntimeReady(); ConditionalConfigSync val = (ConditionalConfigSync)((configSyncInstance is ConditionalConfigSync) ? configSyncInstance : null); if (val == null) { throw new ArgumentException("The supplied context was not created by Conditional Config Sync.", "configSyncInstance"); } if (configEntry == null) { throw new ArgumentNullException("configEntry"); } Type settingType = configEntry.SettingType; if (!typeof(IConvertible).IsAssignableFrom(settingType)) { throw new ArgumentException("Locking config type '" + settingType.FullName + "' must implement IConvertible.", "configEntry"); } MethodInfo closedMethod = GetClosedMethod(AddLockingConfigEntryDefinition, AddLockingConfigEntryMethods, settingType); Invoke(closedMethod, val, new object[1] { configEntry }); } private static MethodInfo GetClosedMethod(MethodInfo definition, Dictionary<Type, MethodInfo> cache, Type settingType) { lock (MethodCacheLock) { if (!cache.TryGetValue(settingType, out MethodInfo value)) { value = (cache[settingType] = definition.MakeGenericMethod(settingType)); } return value; } } private static void Invoke(MethodInfo method, object target, object[] arguments) { try { method.Invoke(target, arguments); } catch (TargetInvocationException ex) when (ex.InnerException != null) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); throw; } } } }