Decompiled source of EWSync v1.0.0

EWSync.dll

Decompiled a day ago
using 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();
		}
	}
}