Decompiled source of Fusion Player Finder v1.0.2

Mods/FusionPlayerFinder.dll

Decompiled 5 days ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BoneLib.BoneMenu;
using FusionPlayerFinder;
using LabFusion.Data;
using LabFusion.Network;
using MelonLoader;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Mod), "Fusion Player Finder", "1.0.2", "Michael", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonAdditionalDependencies(new string[] { "BoneLib", "LabFusion" })]
[assembly: MelonPriority(1000)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("FusionPlayerFinder")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: AssemblyProduct("FusionPlayerFinder")]
[assembly: AssemblyTitle("FusionPlayerFinder")]
[assembly: AssemblyVersion("1.0.2.0")]
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;
		}
	}
}
namespace FusionPlayerFinder
{
	public sealed class Mod : MelonMod
	{
		private readonly ConcurrentQueue<Action> _pending = new ConcurrentQueue<Action>();

		private Page? _page;

		private Page? _results;

		private FunctionElement? _status;

		private StringElement? _username;

		private string? _lastInput;

		private string _query = "";

		private bool _partial;

		private bool _auto;

		private bool _busy;

		private bool _initFailed;

		private int _generation;

		private DateTime _deadline;

		private DateTime _nextScan;

		private DateTime _lastRequest = DateTime.MinValue;

		private NetworkLayer? _requestLayer;

		public override void OnUpdate()
		{
			if (_page == null && !_initFailed && Page.Root != null)
			{
				try
				{
					BuildMenu();
				}
				catch (Exception value)
				{
					_initFailed = true;
					((MelonBase)this).LoggerInstance.Error($"Menu creation failed: {value}");
				}
			}
			Action result;
			while (_pending.TryDequeue(out result))
			{
				try
				{
					result();
				}
				catch (Exception ex)
				{
					Fail("Could not read lobby results; see log.", ex);
				}
			}
			if (_page != null)
			{
				SyncInput();
				if (_busy && (_requestLayer != NetworkLayerManager.Layer || !NetworkLayerManager.LoggedIn))
				{
					Fail("Fusion connection changed. Search again.");
				}
				if (_busy && DateTime.UtcNow >= _deadline)
				{
					Fail("Search timed out. Try again.");
				}
				if (_auto && !_busy && DateTime.UtcNow >= _nextScan && _query.Length > 0)
				{
					Search();
				}
			}
		}

		private void BuildMenu()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			_page = Page.Root.CreatePage("Fusion Player Finder", Color.cyan, 0, true);
			_username = _page.CreateString("Username", Color.white, "", (Action<string>)delegate
			{
				SyncInput();
			});
			_page.CreateFunction("Search public lobbies", Color.green, (Action)Search);
			_page.CreateBool("Partial name match", Color.white, false, (Action<bool>)delegate(bool value)
			{
				_partial = value;
				Reset("Match setting changed. Search again.");
			});
			_page.CreateBool("Refresh every 30 seconds", Color.white, false, (Action<bool>)delegate(bool value)
			{
				_auto = value;
				_nextScan = DateTime.UtcNow;
			});
			_status = _page.CreateFunction("Enter a username to begin.", Color.yellow, (Action)delegate
			{
			});
			_results = _page.CreatePage("Results", Color.cyan, 8, true);
			_page.CreateFunction("Searches your active Fusion network layer", Color.gray, (Action)delegate
			{
			});
			((MelonBase)this).LoggerInstance.Msg("v1.0.2 ready: BoneMenu > Fusion Player Finder. Search diagnostics enabled.");
		}

		private void SyncInput()
		{
			if (_username != null)
			{
				string text = _username.Value ?? "";
				if (!(text == _lastInput))
				{
					_lastInput = text;
					_query = NameMatch.Plain(text);
					Reset((_query.Length == 0) ? "Enter a username to begin." : "Name set. Press Search public lobbies.");
					((MelonBase)this).LoggerInstance.Msg($"Input updated: {_query.Length} characters; partial match={_partial}.");
				}
			}
		}

		private void Reset(string message)
		{
			_generation++;
			_busy = false;
			Page? results = _results;
			if (results != null)
			{
				results.RemoveAll();
			}
			SetStatus(message);
			_nextScan = DateTime.UtcNow.AddSeconds(30.0);
		}

		private void SetStatus(string text)
		{
			if (_status != null)
			{
				((Element)_status).ElementName = text;
			}
			((MelonBase)this).LoggerInstance.Msg(text);
		}

		private void Fail(string message, Exception? ex = null)
		{
			_generation++;
			_busy = false;
			_nextScan = DateTime.UtcNow.AddSeconds(30.0);
			Page? results = _results;
			if (results != null)
			{
				results.RemoveAll();
			}
			SetStatus(message);
			if (ex != null)
			{
				((MelonBase)this).LoggerInstance.Error(ex.ToString());
			}
		}

		private void Search()
		{
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			SyncInput();
			if (_busy)
			{
				return;
			}
			_nextScan = DateTime.UtcNow.AddSeconds(30.0);
			if (_query.Length == 0)
			{
				SetStatus("Enter a username first.");
				return;
			}
			if ((DateTime.UtcNow - _lastRequest).TotalSeconds < 5.0)
			{
				SetStatus("Wait 5 seconds between searches.");
				return;
			}
			NetworkLayer layer = NetworkLayerManager.Layer;
			NetworkLayer obj = layer;
			if (((obj != null) ? obj.Matchmaker : null) == null || !NetworkLayerManager.LoggedIn)
			{
				Fail("Fusion is not connected. Log into Fusion first.");
				return;
			}
			int generation = ++_generation;
			string query = _query;
			bool partial = _partial;
			_requestLayer = layer;
			_busy = true;
			_lastRequest = DateTime.UtcNow;
			_deadline = DateTime.UtcNow.AddSeconds(20.0);
			Page? results = _results;
			if (results != null)
			{
				results.RemoveAll();
			}
			SetStatus("Searching " + NameMatch.Plain(layer.Title) + "...");
			((MelonBase)this).LoggerInstance.Msg($"Request {_generation}: layer={((object)layer).GetType().FullName}, loggedIn={NetworkLayerManager.LoggedIn}, queryLength={query.Length}, partial={partial}.");
			try
			{
				layer.Matchmaker.RequestLobbies(MatchmakerFilters.Empty, (Action<MatchmakerCallbackInfo>)delegate(MatchmakerCallbackInfo response)
				{
					//IL_000e: Unknown result type (might be due to invalid IL or missing references)
					//IL_000f: Unknown result type (might be due to invalid IL or missing references)
					_pending.Enqueue(delegate
					{
						//IL_009f: Unknown result type (might be due to invalid IL or missing references)
						if (generation == _generation && _busy)
						{
							if (DateTime.UtcNow >= _deadline)
							{
								Fail("Search timed out. Try again.");
							}
							else if (layer != NetworkLayerManager.Layer || !NetworkLayerManager.LoggedIn)
							{
								Fail("Fusion connection changed. Search again.");
							}
							else
							{
								ShowResults(response, query, partial);
							}
						}
					});
				});
			}
			catch (Exception ex)
			{
				Fail("Search failed; see MelonLoader log.", ex);
			}
		}

		private void ShowResults(MatchmakerCallbackInfo response, string query, bool partial)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_039d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0566: Unknown result type (might be due to invalid IL or missing references)
			//IL_0551: Unknown result type (might be due to invalid IL or missing references)
			//IL_0516: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			_busy = false;
			_nextScan = DateTime.UtcNow.AddSeconds(30.0);
			_results.RemoveAll();
			HashSet<ulong> hashSet = new HashSet<ulong>();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			int num6 = 0;
			LobbyInfo[] array = response.Lobbies ?? Array.Empty<LobbyInfo>();
			for (int i = 0; i < array.Length; i++)
			{
				LobbyMetadataInfo metadata = array[i].Metadata;
				LobbyInfo lobbyInfo = ((LobbyMetadataInfo)(ref metadata)).LobbyInfo;
				if (!((LobbyMetadataInfo)(ref metadata)).HasLobbyOpen || (int)((LobbyMetadataInfo)(ref metadata)).Privacy != 0 || lobbyInfo == null || (int)lobbyInfo.Privacy != 0)
				{
					num5++;
					continue;
				}
				if (!hashSet.Add(lobbyInfo.LobbyID))
				{
					num6++;
					continue;
				}
				num++;
				PlayerList playerList = lobbyInfo.PlayerList;
				PlayerInfo[] array2 = ((playerList != null) ? playerList.Players : null) ?? Array.Empty<PlayerInfo>();
				num3 += array2.Count((PlayerInfo p) => p != null && (!string.IsNullOrWhiteSpace(p.Username) || !string.IsNullOrWhiteSpace(p.Nickname)));
				if (array2.Length == 0)
				{
					num4++;
				}
				string[] array3 = (from p in array2
					where p != null && (NameMatch.Matches(p.Username, query, partial) || NameMatch.Matches(p.Nickname, query, partial))
					select (!string.IsNullOrWhiteSpace(p.Nickname)) ? (NameMatch.Plain(p.Username) + " (" + NameMatch.Plain(p.Nickname) + ")") : NameMatch.Plain(p.Username)).Distinct().ToArray();
				if (array3.Length == 0 && NameMatch.Matches(lobbyInfo.LobbyHostName, query, partial))
				{
					array3 = new string[1] { NameMatch.Plain(lobbyInfo.LobbyHostName) + " (host)" };
				}
				if (array3.Length != 0)
				{
					num2++;
					Row($"{num2}. {NameMatch.Plain(lobbyInfo.LobbyName)}", Color.cyan);
					string[] array4 = array3;
					foreach (string text in array4)
					{
						Row("Player: " + text, Color.green);
					}
					Row("Host: " + NameMatch.Plain(lobbyInfo.LobbyHostName));
					Row("Map: " + NameMatch.Plain(lobbyInfo.LevelTitle));
					Row($"Players: {lobbyInfo.PlayerCount}/{lobbyInfo.MaxPlayers}");
					Row("Lobby code: " + NameMatch.Plain(lobbyInfo.LobbyCode ?? ((LobbyMetadataInfo)(ref metadata)).LobbyCode), Color.yellow);
					Row($"Fusion version: {lobbyInfo.LobbyVersion}");
				}
			}
			string value = DateTime.Now.ToString("HH:mm:ss");
			Instance loggerInstance = ((MelonBase)this).LoggerInstance;
			DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(97, 7);
			defaultInterpolatedStringHandler.AppendLiteral("Response: returned=");
			LobbyInfo[] lobbies = response.Lobbies;
			defaultInterpolatedStringHandler.AppendFormatted((lobbies != null) ? lobbies.Length : 0);
			defaultInterpolatedStringHandler.AppendLiteral(", public=");
			defaultInterpolatedStringHandler.AppendFormatted(num);
			defaultInterpolatedStringHandler.AppendLiteral(", rejected=");
			defaultInterpolatedStringHandler.AppendFormatted(num5);
			defaultInterpolatedStringHandler.AppendLiteral(", duplicates=");
			defaultInterpolatedStringHandler.AppendFormatted(num6);
			defaultInterpolatedStringHandler.AppendLiteral(", namedPlayers=");
			defaultInterpolatedStringHandler.AppendFormatted(num3);
			defaultInterpolatedStringHandler.AppendLiteral(", emptyPlayerLists=");
			defaultInterpolatedStringHandler.AppendFormatted(num4);
			defaultInterpolatedStringHandler.AppendLiteral(", matches=");
			defaultInterpolatedStringHandler.AppendFormatted(num2);
			defaultInterpolatedStringHandler.AppendLiteral(".");
			loggerInstance.Msg(defaultInterpolatedStringHandler.ToStringAndClear());
			SetStatus($"{num2} matching lobbies / {num} public — {value}");
			if (num2 == 0)
			{
				Row((num == 0) ? "No public lobbies returned. Check Fusion connection." : "No matching names in returned public lobbies.", Color.yellow);
			}
			Row($"Checked {num3} player entries in {num} public lobbies.");
			if (num2 == 0 && !_partial)
			{
				Row("Try enabling Partial name match and search again.", Color.yellow);
			}
			if (num4 > 0)
			{
				Row($"{num4} lobbies did not advertise a player list.", Color.yellow);
			}
			Row("Names can be duplicated; results are advertised snapshots.", Color.gray);
		}

		private void Row(string text, Color? color = null)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			_results.CreateFunction(text, (Color)(((??)color) ?? Color.white), (Action)delegate
			{
			});
		}

		public override void OnDeinitializeMelon()
		{
			_auto = false;
			_generation++;
			_busy = false;
		}
	}
	internal static class NameMatch
	{
		private static readonly Regex Tags = new Regex("<[^>]*>", RegexOptions.CultureInvariant, TimeSpan.FromMilliseconds(100.0));

		public static string Plain(string? value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return "";
			}
			value = value.Substring(0, Math.Min(value.Length, 512));
			try
			{
				value = Tags.Replace(value, "");
			}
			catch (RegexMatchTimeoutException)
			{
				return "";
			}
			return new string(value.Where((char c) => !char.IsControl(c) && c != '<' && c != '>').ToArray()).Trim();
		}

		public static bool Matches(string? name, string query, bool partial)
		{
			string text = Plain(name);
			query = Plain(query);
			if (query.Length > 0)
			{
				if (!partial)
				{
					return string.Equals(text, query, StringComparison.OrdinalIgnoreCase);
				}
				return text.Contains(query, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}
	}
}