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 EWSync v1.0.0
EWSync.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExpandWorld.Music; using ExpandWorldData; using HarmonyLib; using Microsoft.CodeAnalysis; using Service; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("RDMods")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Client-side Expand World biome music and environment sync fix for multiplayer.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("EWSync")] [assembly: AssemblyTitle("EWSync")] [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 RDMods.EWSync { internal static class BiomeSyncService { internal static void Apply() { BiomeManager.LoadEnvironments(); EnvMan instance = EnvMan.instance; if ((Object)(object)instance != (Object)null) { ValheimAccess.ResetEnvironmentCycle(instance); } RefreshMusicIfNeeded(); WorldSyncSession.MarkApplied(); if (WorldSyncConfig.EnableDiagnostics.Value) { EWSyncPlugin.LogInfo("EWSync applied biome environments on client."); } } internal static VerifyResult Verify() { VerifyResult verifyResult = new VerifyResult(); if (WorldSyncConfig.ParsedRules.Count == 0) { verifyResult.AddFailure("No verification rules configured."); return verifyResult; } EnvMan instance = EnvMan.instance; if ((Object)(object)instance == (Object)null) { verifyResult.AddFailure("EnvMan.instance is null."); return verifyResult; } foreach (VerificationRule parsedRule in WorldSyncConfig.ParsedRules) { VerifyRule(parsedRule, instance, verifyResult); } return verifyResult; } private static void VerifyRule(VerificationRule rule, EnvMan envMan, VerifyResult result) { Biome biomeFlag = default(Biome); if (!BiomeManager.TryGetBiome(rule.BiomeName, ref biomeFlag)) { result.AddFailure(rule.BiomeName + ": unknown biome name."); result.RuleSummaries.Add(" " + rule.BiomeName + ": FAIL (unknown biome)"); return; } BiomeEnvSetup val = envMan.m_biomes?.FirstOrDefault((Func<BiomeEnvSetup, bool>)((BiomeEnvSetup b) => b.m_biome == biomeFlag)); if (val == null) { result.AddFailure(rule.BiomeName + ": biome setup missing from EnvMan."); result.RuleSummaries.Add(" " + rule.BiomeName + ": FAIL (missing setup)"); return; } bool flag = true; List<string> list = new List<string>(); if (!MatchesOptional(val.m_musicDay, rule.MusicDay)) { flag = false; result.AddFailure(rule.BiomeName + ": musicDay expected '" + (rule.MusicDay ?? "-") + "', got '" + (val.m_musicDay ?? "(null)") + "'."); list.Add("musicDay FAIL (got " + (val.m_musicDay ?? "null") + ")"); } else { list.Add("musicDay OK"); } if (!MatchesOptional(val.m_musicNight, rule.MusicNight)) { flag = false; result.AddFailure(rule.BiomeName + ": musicNight expected '" + (rule.MusicNight ?? "-") + "', got '" + (val.m_musicNight ?? "(null)") + "'."); list.Add("musicNight FAIL (got " + (val.m_musicNight ?? "null") + ")"); } else { list.Add("musicNight OK"); } foreach (string envName in rule.RequiredEnvironments) { if (val.m_environments == null || !val.m_environments.Any((EnvEntry e) => e.m_environment == envName && e.m_weight > 0f)) { flag = false; result.AddFailure(rule.BiomeName + ": environment '" + envName + "' missing from biome weights."); list.Add("env " + envName + " FAIL"); } else { list.Add("env " + envName + " OK"); } } foreach (string requiredPool in rule.RequiredPools) { if (!IsPoolReady(requiredPool, out string detail)) { flag = false; result.AddFailure(rule.BiomeName + ": music pool '" + requiredPool + "' " + detail + "."); list.Add("pool " + requiredPool + " FAIL (" + detail + ")"); } else { list.Add("pool " + requiredPool + " OK"); } } result.RuleSummaries.Add(" " + rule.BiomeName + ": " + (flag ? "OK" : "FAIL") + " (" + string.Join(", ", list) + ")"); } private static bool MatchesOptional(string? actual, string? expected) { string obj = (string.IsNullOrWhiteSpace(actual) ? "" : actual); string text = expected ?? ""; return obj == text; } private static bool IsPoolReady(string poolName, out string detail) { MusicMan instance = MusicMan.instance; if ((Object)(object)instance == (Object)null) { detail = "MusicMan missing"; return false; } int stableHashCode = StringExtensionMethods.GetStableHashCode(poolName); Dictionary<int, NamedMusic> musicHashes = ValheimAccess.GetMusicHashes(instance); if (musicHashes == null) { detail = "music table missing"; return false; } if (!musicHashes.TryGetValue(stableHashCode, out var value) || value == null) { detail = "not registered"; return false; } if (value.m_clips == null || value.m_clips.Length == 0 || (Object)(object)value.m_clips[0] == (Object)null) { detail = "no clips loaded"; return false; } int num = value.m_clips.Count((AudioClip c) => (Object)(object)c != (Object)null); detail = $"{num} clip(s)"; return true; } private static void RefreshMusicIfNeeded() { MusicMan instance = MusicMan.instance; if (!((Object)(object)instance == (Object)null)) { string text = ValheimAccess.GetCurrentMusic(instance)?.m_name ?? ""; instance.Reset(); if (!string.IsNullOrEmpty(text)) { ValheimAccess.StartMusic(instance, text); } } } internal static string? GetCurrentMusicName() { return ValheimAccess.GetCurrentMusic(MusicMan.instance)?.m_name; } internal static string? GetCurrentMusicClipName() { NamedMusic val = (((Object)(object)MusicMan.instance == (Object)null) ? null : ValheimAccess.GetCurrentMusic(MusicMan.instance)); if (val?.m_clips == null || val.m_clips.Length == 0) { return null; } AudioClip obj = val.m_clips[0]; if (obj == null) { return null; } return ((Object)obj).name; } internal static string? GetCurrentEnvironmentName() { EnvMan instance = EnvMan.instance; if (instance == null) { return null; } return instance.GetCurrentEnvironment()?.m_name; } internal static bool IsEnvironmentRegistered(string environmentName) { EnvMan instance = EnvMan.instance; if (instance == null) { return false; } return instance.m_environments?.Any((EnvSetup e) => e.m_name == environmentName) == true; } } [BepInPlugin("rdmods.ewsync", "EWSync", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class EWSyncPlugin : BaseUnityPlugin { private Harmony? _harmony; internal static EWSyncPlugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; WorldSyncConfig.Init(((BaseUnityPlugin)this).Config); if (!WorldSyncConfig.Enabled.Value) { LogInfo("EWSync is disabled in config."); return; } _harmony = Harmony.CreateAndPatchAll(typeof(EWSyncPlugin).Assembly, (string)null); LogInfo("EWSync v1.0.0 loaded."); } private void OnDestroy() { WorldSyncSession.Reset(); Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } internal static void LogInfo(string message) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)message); } } internal void ApplyAfterBiomeLoad() { if (WorldSyncConfig.Enabled.Value && NetworkRole.IsMultiplayerClient()) { WorldSyncSession.MarkBiomeDataReceived(); BiomeSyncService.Apply(); VerifyResult verifyResult = BiomeSyncService.Verify(); WorldSyncSession.MarkVerified(verifyResult); if (WorldSyncConfig.EnableDiagnostics.Value) { LogInfo(verifyResult.Passed ? "EWSync verify passed after biome apply." : ("EWSync verify failed after biome apply: " + verifyResult.FormatFailures())); } } } internal void ScheduleSpawnSafetyRetry() { if (WorldSyncConfig.Enabled.Value && WorldSyncConfig.SpawnSafetyRetry.Value && NetworkRole.IsMultiplayerClient() && !WorldSyncSession.IsVerified && !WorldSyncSession.SpawnRetryConsumed && !WorldSyncSession.SpawnRetryScheduled) { WorldSyncSession.SpawnRetryScheduled = true; ((MonoBehaviour)this).StartCoroutine(DelayedSpawnRetry()); } } private IEnumerator DelayedSpawnRetry() { float value = WorldSyncConfig.SpawnRetryDelaySeconds.Value; if (value > 0f) { yield return (object)new WaitForSeconds(value); } WorldSyncSession.MarkSpawnRetryConsumed(); if (WorldSyncConfig.Enabled.Value && NetworkRole.IsMultiplayerClient() && !WorldSyncSession.IsVerified) { BiomeSyncService.Apply(); VerifyResult verifyResult = BiomeSyncService.Verify(); WorldSyncSession.MarkVerified(verifyResult); if (WorldSyncConfig.EnableDiagnostics.Value) { LogInfo(verifyResult.Passed ? "EWSync verify passed after spawn safety retry." : ("EWSync verify failed after spawn safety retry: " + verifyResult.FormatFailures())); } } } } internal static class NetworkRole { internal static bool IsMultiplayerClient() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if ((Object)(object)ZNet.instance != (Object)null && (int)ZNet.GetConnectionStatus() == 2) { return !ZNet.instance.IsServer(); } return false; } internal static string DescribeMode() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 if ((Object)(object)ZNet.instance == (Object)null || (int)ZNet.GetConnectionStatus() != 2) { return "Offline / Menu"; } if (!ZNet.instance.IsServer()) { return "Multiplayer Client"; } return "Host / Solo Server"; } internal static bool PatchesActive() { if (WorldSyncConfig.Enabled.Value) { return IsMultiplayerClient(); } return false; } } internal static class PluginInfo { internal const string Guid = "rdmods.ewsync"; internal const string Name = "EWSync"; internal const string Version = "1.0.0"; } internal static class ValheimAccess { private static readonly FieldInfo EnvPeriodField = AccessTools.Field(typeof(EnvMan), "m_environmentPeriod"); private static readonly FieldInfo EnvFirstField = AccessTools.Field(typeof(EnvMan), "m_firstEnv"); private static readonly FieldInfo MusicHashesField = AccessTools.Field(typeof(MusicMan), "m_musicHashes"); private static readonly FieldInfo CurrentMusicField = AccessTools.Field(typeof(MusicMan), "m_currentMusic"); private static readonly MethodInfo StartMusicMethod = AccessTools.Method(typeof(MusicMan), "StartMusic", new Type[1] { typeof(string) }, (Type[])null); internal static void ResetEnvironmentCycle(EnvMan envMan) { EnvPeriodField.SetValue(envMan, -1L); EnvFirstField.SetValue(envMan, true); } internal static Dictionary<int, NamedMusic>? GetMusicHashes(MusicMan musicMan) { return MusicHashesField.GetValue(musicMan) as Dictionary<int, NamedMusic>; } internal static NamedMusic? GetCurrentMusic(MusicMan musicMan) { object? value = CurrentMusicField.GetValue(musicMan); return (NamedMusic?)((value is NamedMusic) ? value : null); } internal static void StartMusic(MusicMan musicMan, string musicName) { StartMusicMethod.Invoke(musicMan, new object[1] { musicName }); } } internal sealed class VerificationRule { internal string BiomeName = ""; internal string? MusicDay; internal string? MusicNight; internal List<string> RequiredEnvironments = new List<string>(); internal List<string> RequiredPools = new List<string>(); internal static List<VerificationRule> Parse(string rulesText, Action<string> logWarning) { List<VerificationRule> list = new List<VerificationRule>(); if (string.IsNullOrWhiteSpace(rulesText)) { return list; } string[] array = rulesText.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length == 0) { continue; } string[] array2 = text.Split(new char[1] { '|' }); if (array2.Length < 2) { logWarning("EWSync: Skipping invalid verify rule (missing fields): " + text); continue; } VerificationRule verificationRule = new VerificationRule { BiomeName = array2[0].Trim() }; for (int j = 1; j < array2.Length; j++) { string text2 = array2[j].Trim(); int num = text2.IndexOf('='); if (num <= 0) { logWarning("EWSync: Skipping invalid token in rule for " + verificationRule.BiomeName + ": " + text2); continue; } string text3 = text2.Substring(0, num).Trim().ToLowerInvariant(); string value = text2.Substring(num + 1).Trim(); switch (text3) { case "musicday": verificationRule.MusicDay = NormalizeOptional(value); break; case "musicnight": verificationRule.MusicNight = NormalizeOptional(value); break; case "env": verificationRule.RequiredEnvironments.AddRange(SplitCsv(value)); break; case "pool": verificationRule.RequiredPools.AddRange(SplitCsv(value)); break; default: logWarning("EWSync: Unknown verify key '" + text3 + "' in rule for " + verificationRule.BiomeName); break; } } if (verificationRule.BiomeName.Length > 0) { list.Add(verificationRule); } } return list; } private static string? NormalizeOptional(string value) { if (string.IsNullOrWhiteSpace(value) || value == "-") { return null; } return value; } private static IEnumerable<string> SplitCsv(string value) { return from v in value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select v.Trim() into v where v.Length > 0 select v; } } internal sealed class VerifyResult { internal bool Passed => Failures.Count == 0; internal List<string> Failures { get; } = new List<string>(); internal List<string> RuleSummaries { get; } = new List<string>(); internal void AddFailure(string message) { Failures.Add(message); } internal string FormatFailures() { if (Failures.Count != 0) { return string.Join("; ", Failures); } return "none"; } internal string FormatRuleSummaries() { if (RuleSummaries.Count != 0) { return string.Join("\n", RuleSummaries); } return "(no rules)"; } } internal static class WorldSyncConfig { private const string DefaultRules = "BlackForest|musicDay=blackforest_custom|musicNight=blackforest_night_custom|env=BlackForest_CinderMist|pool=blackforest_custom,blackforest_night_custom;Meadows|musicDay=meadows_day_custom|musicNight=-|pool=meadows_day_custom,mmoheim_morning;Swamp|musicDay=swamp_custom|musicNight=swamp_custom|pool=swamp_custom;Mountain|musicDay=mountain_custom|musicNight=mountain_custom|pool=mountain_custom;Plains|musicDay=plains_custom|musicNight=plains_night_custom|pool=plains_custom,plains_night_custom;Mistlands|musicDay=mistlands_custom|musicNight=mistlands_custom|pool=mistlands_custom;AshLands|musicDay=ashlands|musicNight=ashlands|pool=ashlands;Ocean|musicDay=ocean_sailing_day|musicNight=ocean_sailing_night|pool=ocean_sailing_day,ocean_sailing_night"; internal static ConfigEntry<bool> Enabled { get; private set; } = null; internal static ConfigEntry<bool> EnableDiagnostics { get; private set; } = null; internal static ConfigEntry<bool> SpawnSafetyRetry { get; private set; } = null; internal static ConfigEntry<float> SpawnRetryDelaySeconds { get; private set; } = null; internal static ConfigEntry<string> Rules { get; private set; } = null; internal static IReadOnlyList<VerificationRule> ParsedRules { get; private set; } = Array.Empty<VerificationRule>(); internal static void Init(ConfigFile config) { Enabled = config.Bind<bool>("General", "Enabled", true, "Master switch for all EWSync client fixes."); EnableDiagnostics = config.Bind<bool>("General", "EnableDiagnostics", false, "Log apply/verify summaries when biome config is applied on clients."); SpawnSafetyRetry = config.Bind<bool>("General", "SpawnSafetyRetry", true, "If verify fails after biome sync, retry once after local player spawns."); SpawnRetryDelaySeconds = config.Bind<float>("General", "SpawnRetryDelaySeconds", 1.5f, "Seconds to wait after OnSpawned before the safety retry."); Rules = config.Bind<string>("Verification", "Rules", "BlackForest|musicDay=blackforest_custom|musicNight=blackforest_night_custom|env=BlackForest_CinderMist|pool=blackforest_custom,blackforest_night_custom;Meadows|musicDay=meadows_day_custom|musicNight=-|pool=meadows_day_custom,mmoheim_morning;Swamp|musicDay=swamp_custom|musicNight=swamp_custom|pool=swamp_custom;Mountain|musicDay=mountain_custom|musicNight=mountain_custom|pool=mountain_custom;Plains|musicDay=plains_custom|musicNight=plains_night_custom|pool=plains_custom,plains_night_custom;Mistlands|musicDay=mistlands_custom|musicNight=mistlands_custom|pool=mistlands_custom;AshLands|musicDay=ashlands|musicNight=ashlands|pool=ashlands;Ocean|musicDay=ocean_sailing_day|musicNight=ocean_sailing_night|pool=ocean_sailing_day,ocean_sailing_night", "Semicolon-separated biome rules. Format per biome: BiomeName|musicDay=x|musicNight=y|env=a,b|pool=p1,p2"); ReloadRules(); Rules.SettingChanged += delegate { ReloadRules(); }; } private static void ReloadRules() { ParsedRules = VerificationRule.Parse(Rules.Value, delegate(string message) { EWSyncPlugin.LogInfo(message); }); } } internal static class WorldSyncSession { internal static bool BiomeDataReceived { get; private set; } internal static bool IsVerified { get; private set; } internal static bool SpawnRetryScheduled { get; set; } internal static bool SpawnRetryConsumed { get; private set; } internal static VerifyResult? LastVerifyResult { get; private set; } internal static DateTime LastApplyTime { get; private set; } internal static void MarkBiomeDataReceived() { BiomeDataReceived = true; } internal static void MarkVerified(VerifyResult result) { LastVerifyResult = result; if (result.Passed) { IsVerified = true; } } internal static void MarkSpawnRetryConsumed() { SpawnRetryConsumed = true; SpawnRetryScheduled = false; } internal static void MarkApplied() { LastApplyTime = DateTime.UtcNow; } internal static void Reset() { BiomeDataReceived = false; IsVerified = false; SpawnRetryScheduled = false; SpawnRetryConsumed = false; LastVerifyResult = null; LastApplyTime = default(DateTime); } } } namespace RDMods.EWSync.Patches { [HarmonyPatch(typeof(BiomeManager), "Load")] internal static class BiomeManager_Load_Patch { private static void Postfix() { EWSyncPlugin.Instance?.ApplyAfterBiomeLoad(); } } [HarmonyPatch(typeof(FejdStartup), "ShowCharacterSelection")] internal static class FejdStartup_ShowCharacterSelection_Patch { private static void Prefix() { WorldSyncSession.Reset(); } } [HarmonyPatch(typeof(Game), "Logout")] internal static class Game_Logout_Patch { private static void Prefix() { WorldSyncSession.Reset(); } } [HarmonyPatch(typeof(MusicManager), "FromSetting")] internal static class MusicManager_FromSetting_Patch { private static void Prefix(List<Data> data) { if (!WorldSyncConfig.Enabled.Value || !NetworkRole.IsMultiplayerClient()) { return; } string text = Yaml.ReadFiles(MusicManager.Pattern); if (string.IsNullOrEmpty(text)) { return; } Dictionary<string, List<string>> dictionary = (from entry in Yaml.Read<Data>(text, "Music", (Action<string>)Log.Warning).ToList() where entry.clips != null && entry.clips.Count > 0 select entry).ToDictionary((Data entry) => entry.name, (Data entry) => entry.clips); if (dictionary.Count == 0) { return; } int num = 0; foreach (Data datum in data) { if (dictionary.TryGetValue(datum.name, out var value) && value != null) { datum.clips = new List<string>(value); num++; } } if (num > 0 && WorldSyncConfig.EnableDiagnostics.Value) { EWSyncPlugin.LogInfo($"EWSync merged local clip paths for {num} music pool(s) on client."); } } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class Player_OnSpawned_Patch { private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { EWSyncPlugin.Instance?.ScheduleSpawnSafetyRetry(); } } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] internal static class ZNet_OnDestroy_Patch { private static void Postfix() { WorldSyncSession.Reset(); } } } namespace RDMods.EWSync.Commands { internal static class SyncCheckCommand { [CompilerGenerated] private static class <>O { public static ConsoleEvent <0>__Run; } internal static void Register() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown object obj = <>O.<0>__Run; if (obj == null) { ConsoleEvent val = Run; <>O.<0>__Run = val; obj = (object)val; } new ConsoleCommand("ewsync_check", "EWSync: print biome sync status, current music, and current weather.", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static void Run(ConsoleEventArgs args) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("=== EWSync (RDMods) ==="); stringBuilder.AppendLine($"Plugin enabled: {WorldSyncConfig.Enabled.Value}"); stringBuilder.AppendLine("Mode: " + NetworkRole.DescribeMode()); stringBuilder.AppendLine($"Patches active: {NetworkRole.PatchesActive()}"); stringBuilder.AppendLine($"Session verified: {WorldSyncSession.IsVerified}"); stringBuilder.AppendLine($"Biome data received: {WorldSyncSession.BiomeDataReceived}"); stringBuilder.AppendLine($"Spawn retry used: {WorldSyncSession.SpawnRetryConsumed}"); stringBuilder.AppendLine($"Verify rules loaded: {WorldSyncConfig.ParsedRules.Count}"); VerifyResult lastVerifyResult = WorldSyncSession.LastVerifyResult; stringBuilder.AppendLine(); stringBuilder.AppendLine("--- Config rules ---"); stringBuilder.AppendLine(lastVerifyResult?.FormatRuleSummaries() ?? "(no verify run yet)"); stringBuilder.AppendLine(); stringBuilder.AppendLine("--- Live runtime ---"); stringBuilder.AppendLine("Current music: " + (BiomeSyncService.GetCurrentMusicName() ?? "(none)")); stringBuilder.AppendLine("Current music clip: " + (BiomeSyncService.GetCurrentMusicClipName() ?? "(none)")); stringBuilder.AppendLine("Current weather: " + (BiomeSyncService.GetCurrentEnvironmentName() ?? "(none)")); stringBuilder.AppendLine(); stringBuilder.AppendLine("--- Last verify ---"); if (lastVerifyResult == null) { stringBuilder.AppendLine("Result: (not run yet)"); } else { stringBuilder.AppendLine("Result: " + (lastVerifyResult.Passed ? "PASSED" : "FAILED")); stringBuilder.AppendLine("Failures: " + lastVerifyResult.FormatFailures()); } if (!NetworkRole.PatchesActive()) { stringBuilder.AppendLine(); stringBuilder.AppendLine("Note: EWSync client patches only run while connected as a non-host client."); } stringBuilder.AppendLine(); stringBuilder.AppendLine("Edit BepInEx/config/rdmods.ewsync.cfg [Verification] Rules if expand_biomes.yaml changes."); string text = stringBuilder.ToString().TrimEnd(Array.Empty<char>()); args.Context.AddString(text); EWSyncPlugin.LogInfo(text.Replace("\n", " | ")); } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] internal static class Terminal_InitTerminal_Patch { private static void Postfix() { SyncCheckCommand.Register(); } } }