using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using Steamworks;
using UnityEngine;
using Whisper.Services;
using whisper;
using whisper.Config;
using whisper.Core;
[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("Whisper")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+1ddb199e8b1ec52a7c91442715b971d231617784")]
[assembly: AssemblyProduct("Whisper")]
[assembly: AssemblyTitle("Whisper")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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 Whisper.Services
{
public sealed class WhisperLogger : IWhisperLogger, IDisposable
{
private readonly ManualLogSource _log = Logger.CreateLogSource(pluginName);
public WhisperLogger(string pluginName)
{
}
private static string FormatMessage(object data, [CanBeNull] string domain)
{
if (!string.IsNullOrWhiteSpace(domain))
{
return $"[{domain}] {data}";
}
return data?.ToString();
}
public void Fatal(object data, string domain = null)
{
_log.LogFatal((object)FormatMessage(data, domain));
}
public void Error(object data, string domain = null)
{
_log.LogError((object)FormatMessage(data, domain));
}
public void Warning(object data, string domain = null)
{
_log.LogWarning((object)FormatMessage(data, domain));
}
public void Message(object data, string domain = null)
{
_log.LogMessage((object)FormatMessage(data, domain));
}
public void Info(object data, string domain = null)
{
_log.LogInfo((object)FormatMessage(data, domain));
}
public void Debug(object data, string domain = null)
{
if (ModSystem.ConfigData.EnableDebugLogs.Value)
{
_log.LogDebug((object)FormatMessage(data, domain));
}
}
public void Dispose()
{
Logger.Sources.Remove((ILogSource)(object)_log);
}
}
}
namespace whisper
{
internal static class Constants
{
public const string PluginName = "Whisper";
public const string PluginGuid = "com.harukadev.magearena.whisper";
}
[BepInProcess("MageArena.exe")]
[BepInPlugin("com.harukadev.magearena.whisper", "Whisper", "1.0.0")]
[BepInIncompatibility("com.magearena.hostsettings")]
public class ModSystem : BaseUnityPlugin
{
internal static IWhisperLogger Log;
internal static WhisperConfig ConfigData;
private Harmony _harmony;
private void Awake()
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Expected O, but got Unknown
Log = new WhisperLogger("Whisper");
ConfigData = new WhisperConfig(((BaseUnityPlugin)this).Config);
_harmony = new Harmony("com.harukadev.magearena.whisper");
_harmony.PatchAll();
Log.Info("is loaded!");
}
private void OnDestroy()
{
Log.Info("mod unloaded");
Log.Dispose();
Harmony harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
}
}
namespace whisper.Patches
{
[HarmonyPatch(typeof(BootstrapManager), "GetLobbiesList")]
internal class PatchGetLobbiesList
{
private const string DomainName = "LobbyDiscovery";
private static bool Prefix(BootstrapManager __instance)
{
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_017b: Unknown result type (might be due to invalid IL or missing references)
if (!ModSystem.ConfigData.Enabled.Value)
{
ModSystem.Log.Debug("Whisper disabled — using vanilla lobby discovery", "LobbyDiscovery");
return true;
}
ModSystem.Log.Debug("Intercepting GetLobbiesList", "LobbyDiscovery");
if (__instance.lobbyIDs.Count > 0)
{
ModSystem.Log.Debug($"Clearing {__instance.lobbyIDs.Count} cached lobby IDs", "LobbyDiscovery");
__instance.lobbyIDs.Clear();
}
SteamMatchmaking.AddRequestLobbyListDistanceFilter(ResolveDistanceFilter(ModSystem.ConfigData.LobbyDistance.Value));
ModSystem.Log.Info($"Applied distance filter: {ModSystem.ConfigData.LobbyDistance.Value}", "LobbyDiscovery");
SteamMatchmaking.AddRequestLobbyListResultCountFilter(Mathf.Clamp(ModSystem.ConfigData.MaxLobbyResults.Value, 1, 250));
ModSystem.Log.Debug($"Result count filter set to {ModSystem.ConfigData.MaxLobbyResults.Value}", "LobbyDiscovery");
string version = Application.version;
if (!ModSystem.ConfigData.IgnoreGameVersion.Value)
{
SteamMatchmaking.AddRequestLobbyListStringFilter("Version", version, (ELobbyComparison)0);
}
SteamMatchmaking.AddRequestLobbyListStringFilter("closed", "0", (ELobbyComparison)0);
string text = (ModSystem.ConfigData.IgnoreGameVersion.Value ? "IGNORED" : version);
ModSystem.Log.Debug("Applied filters: Version=" + text + ", closed=0", "LobbyDiscovery");
SteamMatchmaking.RequestLobbyList();
ModSystem.Log.Info("Lobby list request sent", "LobbyDiscovery");
return false;
}
private static ELobbyDistanceFilter ResolveDistanceFilter(FilterOptionsEnum option)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
return (ELobbyDistanceFilter)(option switch
{
FilterOptionsEnum.SameRegion => 0,
FilterOptionsEnum.NearbyRegion => 1,
FilterOptionsEnum.Continent => 2,
FilterOptionsEnum.Global => 3,
_ => 3,
});
}
}
[HarmonyPatch(typeof(BootstrapManager), "OnGetLobbyList")]
internal static class PatchOnGetLobbyList
{
private const string Domain = "LobbyDiscovery.OnGetLobbyList";
private static float _lastInterceptTime;
private static bool Prefix(BootstrapManager __instance, LobbyMatchList_t result)
{
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_0107: Unknown result type (might be due to invalid IL or missing references)
//IL_010d: Unknown result type (might be due to invalid IL or missing references)
//IL_011e: Unknown result type (might be due to invalid IL or missing references)
if (!ModSystem.ConfigData.Enabled.Value)
{
ModSystem.Log.Debug("Whisper disabled — falling back to vanilla lobby discovery", "LobbyDiscovery.OnGetLobbyList");
return true;
}
if (Time.realtimeSinceStartup - _lastInterceptTime < 0.5f)
{
ModSystem.Log.Debug("Skipping lobby intercept (rate limited)", "LobbyDiscovery.OnGetLobbyList");
return true;
}
_lastInterceptTime = Time.realtimeSinceStartup;
if ((Object)(object)FindPublicLobbies.instance == (Object)null)
{
ModSystem.Log.Warning("FindPublicLobbies instance not available — aborting custom injection", "LobbyDiscovery.OnGetLobbyList");
return true;
}
uint nLobbiesMatching = result.m_nLobbiesMatching;
ModSystem.Log.Info($"Steam returned {nLobbiesMatching} lobbies — injecting without vanilla filters", "LobbyDiscovery.OnGetLobbyList");
if (nLobbiesMatching == 0)
{
ModSystem.Log.Warning("No lobbies returned — falling back to vanilla behavior", "LobbyDiscovery.OnGetLobbyList");
return true;
}
if (FindPublicLobbies.instance.listOfLobbies.Count > 0)
{
ModSystem.Log.Debug("Destroying cached lobbies", "LobbyDiscovery.OnGetLobbyList");
FindPublicLobbies.instance.DestroyLobbies();
}
__instance.lobbyIDs.Clear();
for (int i = 0; i < nLobbiesMatching; i++)
{
CSteamID lobbyByIndex = SteamMatchmaking.GetLobbyByIndex(i);
__instance.lobbyIDs.Add(lobbyByIndex);
SteamMatchmaking.RequestLobbyData(lobbyByIndex);
ModSystem.Log.Debug($"Injected lobby {lobbyByIndex.m_SteamID}", "LobbyDiscovery.OnGetLobbyList");
}
ModSystem.Log.Info($"Lobby injection complete — injected {__instance.lobbyIDs.Count}", "LobbyDiscovery.OnGetLobbyList");
return false;
}
}
}
namespace whisper.Core
{
public interface IWhisperLogger : IDisposable
{
void Fatal(object data, [CanBeNull] string domain = null);
void Error(object data, [CanBeNull] string domain = null);
void Warning(object data, [CanBeNull] string domain = null);
void Message(object data, [CanBeNull] string domain = null);
void Info(object data, [CanBeNull] string domain = null);
void Debug(object data, [CanBeNull] string domain = null);
}
}
namespace whisper.Config
{
public class WhisperConfig
{
public readonly ConfigEntry<bool> Enabled = config.Bind<bool>("General", "Enabled", true, "Toggle the mod");
public readonly ConfigEntry<FilterOptionsEnum> LobbyDistance = config.Bind<FilterOptionsEnum>("Lobby Discovery", "LobbyDistance", FilterOptionsEnum.Global, "How far Steam should search for lobbies.");
public readonly ConfigEntry<int> MaxLobbyResults = config.Bind<int>("Lobby Discovery", "MaxLobbyResults", 100, new ConfigDescription("Maximum number of lobbies requested from Steam.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(20, 250), Array.Empty<object>()));
public readonly ConfigEntry<bool> IgnoreLobbySizeFilter = config.Bind<bool>("Lobby Discovery", "IgnoreLobbySizeFilter", true, "Ignores the game's original lobby size filter (2p / 8p).");
public readonly ConfigEntry<bool> IgnoreGameVersion = config.Bind<bool>("Compatibility", "IgnoreGameVersion", false, "Allows joining lobbies with different game versions (unsafe).");
public readonly ConfigEntry<bool> DisableWhisperPatch = config.Bind<bool>("Compatibility", "DisableWhisperPatch", false, "Disables Whisper lobby discovery and uses the game's original behavior.");
public readonly ConfigEntry<bool> EnableDebugLogs = config.Bind<bool>("Debug", "EnableDebugLogs", false, "Enables verbose debug logging.");
public WhisperConfig(ConfigFile config)
{
}//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Expected O, but got Unknown
}
public enum FilterOptionsEnum
{
SameRegion,
NearbyRegion,
Continent,
Global
}
}