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;
}
}
}