Decompiled source of BiomeMusicSync v0.5.0

BiomeMusicSync.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using HarmonyLib;
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("BiomeMusicSync")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BiomeMusicSync")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("97d15fa2-3931-4f97-8b2f-5611fc7880a1")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("ezat.valheim.biomemusicsync", "BiomeMusicSync", "0.5.0")]
public class BiomeMusicSync : BaseUnityPlugin
{
	private const string RpcName = "Ezat_BiomeMusicSync_State_v4";

	private static BiomeMusicSync _instance;

	private static readonly BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

	private const float HostCheckInterval = 0.1f;

	private const float HostHeartbeat = 1f;

	private const float ClientInterval = 0.1f;

	private const float StateTimeout = 4f;

	private const float HardSeekThreshold = 0.5f;

	private const float DriftDeadzone = 0.02f;

	private const float PitchGain = 0.1f;

	private const float MaxPitchOffset = 0.02f;

	private const float StartCooldown = 1.5f;

	private static ZRoutedRpc _registeredFor;

	private static float _nextHostCheck;

	private static float _nextClient;

	private static float _lastSend;

	private static float _nextStart;

	private static string _sentBiome = "";

	private static string _sentTrack = "";

	private static int _sentClip = -2;

	private static bool _sentPlaying;

	private static bool _hasState;

	private static float _lastRecv;

	private static string _hostBiome = "";

	private static string _hostTrack = "";

	private static int _hostClip = -1;

	private static bool _hostPlaying;

	private static double _t0;

	private static bool _pitchTouched;

	private static MethodInfo _getTimeSeconds;

	private static bool _netTimeChecked;

	private void Awake()
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Expected O, but got Unknown
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Expected O, but got Unknown
		_instance = this;
		try
		{
			Harmony val = new Harmony("ezat.valheim.biomemusicsync");
			HarmonyMethod val2 = new HarmonyMethod(typeof(BiomeMusicSync).GetMethod("StartMusicPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			int num = 0;
			MethodInfo[] methods = typeof(MusicMan).GetMethods(Flags);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name == "StartMusic" && methodInfo.DeclaringType == typeof(MusicMan))
				{
					val.Patch((MethodBase)methodInfo, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("BiomeMusicSync loaded. StartMusic patches: " + num));
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Harmony patch failed: " + ex.Message));
		}
	}

	private void Update()
	{
		if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null)
		{
			return;
		}
		RegisterRpc();
		if (ZNet.instance.IsServer())
		{
			if (Time.unscaledTime >= _nextHostCheck)
			{
				_nextHostCheck = Time.unscaledTime + 0.1f;
				HostTick();
			}
		}
		else if (Time.unscaledTime >= _nextClient)
		{
			_nextClient = Time.unscaledTime + 0.1f;
			ClientTick();
		}
	}

	private static void RegisterRpc()
	{
		if (ZRoutedRpc.instance != null && _registeredFor != ZRoutedRpc.instance)
		{
			ZRoutedRpc.instance.Register<ZPackage>("Ezat_BiomeMusicSync_State_v4", (Action<long, ZPackage>)RPC_ReceiveMusicState);
			_registeredFor = ZRoutedRpc.instance;
			_hasState = false;
			_sentClip = -2;
			_sentTrack = "";
			_sentBiome = "";
			ResetPitch();
		}
	}

	private static double Now()
	{
		if (!_netTimeChecked)
		{
			_netTimeChecked = true;
			try
			{
				_getTimeSeconds = AccessTools.Method(typeof(ZNet), "GetTimeSeconds", (Type[])null, (Type[])null);
			}
			catch
			{
				_getTimeSeconds = null;
			}
		}
		if (_getTimeSeconds != null && (Object)(object)ZNet.instance != (Object)null)
		{
			try
			{
				return Convert.ToDouble(_getTimeSeconds.Invoke(ZNet.instance, null));
			}
			catch
			{
			}
		}
		return Time.unscaledTimeAsDouble;
	}

	private static object GetMember(object instance, string name)
	{
		if (instance == null)
		{
			return null;
		}
		Type type = instance.GetType();
		FieldInfo field = type.GetField(name, Flags);
		if (field != null)
		{
			return field.GetValue(instance);
		}
		PropertyInfo property = type.GetProperty(name, Flags);
		if (property != null)
		{
			return property.GetValue(instance, null);
		}
		return null;
	}

	private static string GetLocalBiome()
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		return ((Object)(object)localPlayer == (Object)null) ? "" : ((object)localPlayer.GetCurrentBiome()/*cast due to .constrained prefix*/).ToString();
	}

	private static object GetCurrentMusic(object manager)
	{
		return GetMember(manager, "m_currentMusic");
	}

	private static AudioSource GetMusicSource(object manager)
	{
		object member = GetMember(manager, "m_musicSource");
		return (AudioSource)((member is AudioSource) ? member : null);
	}

	private static IList GetMusicList(object manager)
	{
		return GetMember(manager, "m_music") as IList;
	}

	private static string GetMusicName(object music)
	{
		if (music == null)
		{
			return "";
		}
		if (music is string result)
		{
			return result;
		}
		return GetMember(music, "m_name")?.ToString() ?? "";
	}

	private static object FindMusic(object manager, string name)
	{
		IList musicList = GetMusicList(manager);
		if (musicList == null || string.IsNullOrEmpty(name))
		{
			return null;
		}
		foreach (object item in musicList)
		{
			if (GetMusicName(item) == name)
			{
				return item;
			}
		}
		return null;
	}

	private static int GetClipIndex(object music, AudioSource source)
	{
		if (music == null || (Object)(object)source == (Object)null || (Object)(object)source.clip == (Object)null)
		{
			return -1;
		}
		if (!(GetMember(music, "m_clips") is IList list))
		{
			return -1;
		}
		for (int i = 0; i < list.Count; i++)
		{
			if (list[i] == source.clip)
			{
				return i;
			}
		}
		return -1;
	}

	private static void StartMusicOnManager(object manager, object music, string name)
	{
		MethodInfo[] methods = manager.GetType().GetMethods(Flags);
		foreach (MethodInfo methodInfo in methods)
		{
			if (methodInfo.Name != "StartMusic")
			{
				continue;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			if (parameters.Length == 1)
			{
				if (music != null && parameters[0].ParameterType.IsAssignableFrom(music.GetType()))
				{
					methodInfo.Invoke(manager, new object[1] { music });
					break;
				}
				if (parameters[0].ParameterType == typeof(string))
				{
					methodInfo.Invoke(manager, new object[1] { name });
					break;
				}
			}
		}
	}

	private static bool SyncActive()
	{
		if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
		{
			return false;
		}
		if (!_hasState || string.IsNullOrEmpty(_hostTrack))
		{
			return false;
		}
		if (Time.unscaledTime - _lastRecv > 4f)
		{
			return false;
		}
		return GetLocalBiome() == _hostBiome;
	}

	private static void StartMusicPrefix(object[] __args)
	{
		try
		{
			if (__args == null || __args.Length < 1 || __args[0] == null || !SyncActive())
			{
				return;
			}
			object obj = __args[0];
			if (GetMusicName(obj) == _hostTrack)
			{
				return;
			}
			if (obj is string)
			{
				__args[0] = _hostTrack;
				return;
			}
			object obj2 = FindMusic(MusicMan.instance, _hostTrack);
			if (obj2 != null && obj.GetType().IsAssignableFrom(obj2.GetType()))
			{
				__args[0] = obj2;
			}
		}
		catch (Exception ex)
		{
			BiomeMusicSync instance = _instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)("StartMusicPrefix: " + ex.Message));
			}
		}
	}

	private static void HostTick()
	{
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Expected O, but got Unknown
		try
		{
			MusicMan instance = MusicMan.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			object currentMusic = GetCurrentMusic(instance);
			AudioSource musicSource = GetMusicSource(instance);
			if (!((Object)(object)musicSource == (Object)null))
			{
				string localBiome = GetLocalBiome();
				string musicName = GetMusicName(currentMusic);
				int clipIndex = GetClipIndex(currentMusic, musicSource);
				bool isPlaying = musicSource.isPlaying;
				if (localBiome != _sentBiome || musicName != _sentTrack || clipIndex != _sentClip || isPlaying != _sentPlaying || !(Time.unscaledTime - _lastSend < 1f))
				{
					_sentBiome = localBiome;
					_sentTrack = musicName;
					_sentClip = clipIndex;
					_sentPlaying = isPlaying;
					_lastSend = Time.unscaledTime;
					double num = Now();
					ZPackage val = new ZPackage();
					val.Write(localBiome);
					val.Write(musicName);
					val.Write(clipIndex);
					val.Write(isPlaying);
					val.Write(num);
					val.Write((double)musicSource.time);
					ZRoutedRpc.instance.InvokeRoutedRPC(0L, "Ezat_BiomeMusicSync_State_v4", new object[1] { val });
				}
			}
		}
		catch (Exception ex)
		{
			BiomeMusicSync instance2 = _instance;
			if (instance2 != null)
			{
				((BaseUnityPlugin)instance2).Logger.LogWarning((object)("Broadcast failed: " + ex.Message));
			}
		}
	}

	private static bool IsFromServer(long sender)
	{
		try
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				object obj = AccessTools.Method(typeof(ZNet), "GetServerPeer", (Type[])null, (Type[])null)?.Invoke(ZNet.instance, null);
				if (obj != null)
				{
					object member = GetMember(obj, "m_uid");
					if (member != null)
					{
						return Convert.ToInt64(member) == sender;
					}
				}
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null);
			if (methodInfo != null && ZRoutedRpc.instance != null)
			{
				object obj2 = methodInfo.Invoke(ZRoutedRpc.instance, null);
				if (obj2 != null)
				{
					long num = Convert.ToInt64(obj2);
					if (num != 0)
					{
						return num == sender;
					}
				}
			}
		}
		catch
		{
		}
		return true;
	}

	private static void RPC_ReceiveMusicState(long sender, ZPackage package)
	{
		try
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && IsFromServer(sender))
			{
				string hostBiome = package.ReadString();
				string text = package.ReadString();
				int num = package.ReadInt();
				bool hostPlaying = package.ReadBool();
				double num2 = package.ReadDouble();
				double num3 = package.ReadDouble();
				double num4 = ((_getTimeSeconds != null || !_netTimeChecked) ? (num2 - num3) : (Now() - num3));
				if (!_hasState || !(text == _hostTrack) || num != _hostClip || Math.Abs(num4 - _t0) > 1.0)
				{
					_t0 = num4;
				}
				else
				{
					_t0 += (num4 - _t0) * 0.2;
				}
				_hostBiome = hostBiome;
				_hostTrack = text;
				_hostClip = num;
				_hostPlaying = hostPlaying;
				_lastRecv = Time.unscaledTime;
				_hasState = true;
			}
		}
		catch (Exception ex)
		{
			BiomeMusicSync instance = _instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)("Receive failed: " + ex.Message));
			}
		}
	}

	private static void ResetPitch()
	{
		if (!_pitchTouched)
		{
			return;
		}
		_pitchTouched = false;
		try
		{
			AudioSource val = (((Object)(object)MusicMan.instance != (Object)null) ? GetMusicSource(MusicMan.instance) : null);
			if ((Object)(object)val != (Object)null)
			{
				val.pitch = 1f;
			}
		}
		catch
		{
		}
	}

	private static void ClientTick()
	{
		try
		{
			if (!SyncActive())
			{
				ResetPitch();
				return;
			}
			MusicMan instance = MusicMan.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			AudioSource musicSource = GetMusicSource(instance);
			if ((Object)(object)musicSource == (Object)null)
			{
				return;
			}
			object obj = FindMusic(instance, _hostTrack);
			if (obj == null)
			{
				return;
			}
			if (GetMusicName(GetCurrentMusic(instance)) != _hostTrack)
			{
				if (Time.unscaledTime >= _nextStart)
				{
					_nextStart = Time.unscaledTime + 1.5f;
					StartMusicOnManager(instance, obj, _hostTrack);
				}
				return;
			}
			if (!_hostPlaying)
			{
				ResetPitch();
				return;
			}
			IList list = GetMember(obj, "m_clips") as IList;
			AudioClip val = null;
			if (list != null && _hostClip >= 0 && _hostClip < list.Count)
			{
				object? obj2 = list[_hostClip];
				val = (AudioClip)((obj2 is AudioClip) ? obj2 : null);
			}
			if ((Object)(object)val == (Object)null || val.length <= 0f)
			{
				return;
			}
			double num = Now() - _t0;
			float length = val.length;
			if (musicSource.loop)
			{
				num %= (double)length;
				if (num < 0.0)
				{
					num += (double)length;
				}
			}
			else
			{
				if (num >= (double)length - 0.05)
				{
					return;
				}
				if (num < 0.0)
				{
					num = 0.0;
				}
			}
			if ((Object)(object)musicSource.clip != (Object)(object)val)
			{
				musicSource.clip = val;
				musicSource.pitch = 1f;
				musicSource.time = (float)num;
				musicSource.Play();
				return;
			}
			if (!musicSource.isPlaying)
			{
				musicSource.time = (float)num;
				musicSource.Play();
				return;
			}
			float num2 = (float)num - musicSource.time;
			if (musicSource.loop)
			{
				if (num2 > length * 0.5f)
				{
					num2 -= length;
				}
				else if (num2 < (0f - length) * 0.5f)
				{
					num2 += length;
				}
			}
			float num3 = Mathf.Abs(num2);
			if (num3 > 0.5f)
			{
				musicSource.time = Mathf.Clamp((float)num, 0f, length - 0.05f);
				musicSource.pitch = 1f;
				_pitchTouched = true;
				return;
			}
			float num4 = 1f;
			if (num3 > 0.02f)
			{
				num4 = 1f + Mathf.Clamp(num2 * 0.1f, -0.02f, 0.02f);
			}
			musicSource.pitch = Mathf.MoveTowards(musicSource.pitch, num4, 0.005f);
			_pitchTouched = true;
		}
		catch (Exception ex)
		{
			BiomeMusicSync instance2 = _instance;
			if (instance2 != null)
			{
				((BaseUnityPlugin)instance2).Logger.LogWarning((object)("Apply failed: " + ex.Message));
			}
		}
	}
}