using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
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 HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("MuckArtifactLobby")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("The lobby host turns artifact mods on and off for every player. Shows nothing without an artifact mod.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+b3815082b9868be7615596cfa6cd60a86ecd2179")]
[assembly: AssemblyProduct("Artifact Lobby")]
[assembly: AssemblyTitle("MuckArtifactLobby")]
[assembly: InternalsVisibleTo("MuckArtifactLobby.Tests")]
[assembly: AssemblyVersion("0.1.0.0")]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
internal sealed class IsReadOnlyAttribute : Attribute
{
}
}
namespace MuckArtifactLobby
{
[BepInPlugin("evan.muck.artifactlobby", "Artifact Lobby", "0.1.0")]
public class ArtifactLobbyPlugin : BaseUnityPlugin
{
public const string Guid = "evan.muck.artifactlobby";
public const string Name = "Artifact Lobby";
public const string Version = "0.1.0";
internal static ManualLogSource Log;
private static readonly HashSet<string> LoggedKeys = new HashSet<string>();
private void Awake()
{
//IL_0018: 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_002d: Expected O, but got Unknown
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Expected O, but got Unknown
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Expected O, but got Unknown
//IL_006c: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
if (SteamLobbyData.TryInit())
{
Harmony val = new Harmony("evan.muck.artifactlobby");
TryPatch(val, typeof(LobbyOpenPatch));
TryPatch(val, typeof(LobbyClosePatch));
TryPatch(val, typeof(StartGamePatch));
TryPatch(val, typeof(LobbySettingsStartPatch));
TryPatch(val, typeof(UpdateSettingPatch));
LobbySession.Changed += SyncPanel;
}
}
private static void SyncPanel()
{
try
{
ArtifactPanel.Sync();
}
catch (Exception arg)
{
LogOnce("sync", $"Artifact Lobby: updating the Artifacts panel failed: {arg}");
}
}
private void Start()
{
if (ArtifactRegistry.All.Count == 0)
{
Log.LogInfo((object)("Artifact Lobby 0.1.0 loaded. " + ArtifactRegistry.Summary()));
return;
}
Log.LogInfo((object)"Artifact Lobby 0.1.0 loaded.");
Log.LogInfo((object)("Artifact Lobby: " + ArtifactRegistry.Summary()));
}
private void Update()
{
try
{
LobbySession.Poll(Time.unscaledTime);
}
catch (Exception arg)
{
LobbySession.Clear();
LogOnce("poll", $"Artifact Lobby: reading the host's artifacts failed: {arg}");
}
}
internal static void LogOnce(string key, string message)
{
if (LoggedKeys.Add(key))
{
ManualLogSource log = Log;
if (log != null)
{
log.LogError((object)message);
}
}
}
private static void TryPatch(Harmony harmony, Type patchClass)
{
try
{
harmony.CreateClassProcessor(patchClass).Patch();
}
catch (Exception arg)
{
Log.LogError((object)$"Artifact Lobby failed to patch {patchClass.Name}: {arg}");
}
}
}
internal static class ArtifactPanel
{
private sealed class Row
{
public ArtifactEntry Entry;
public UiSettings Setting;
public Component Description;
}
private static readonly Color DescriptionColor = new Color(0.8f, 0.8f, 0.8f, 1f);
private static readonly string[] OffOn = new string[2] { "Off", "On" };
private static GameObject button;
private static GameObject panel;
private static Component title;
private static readonly List<Row> Rows = new List<Row>();
private static bool open;
private static bool Built
{
get
{
if ((Object)(object)button != (Object)null)
{
return (Object)(object)panel != (Object)null;
}
return false;
}
}
public static void Build(LobbySettings settings)
{
//IL_014d: Unknown result type (might be due to invalid IL or missing references)
//IL_015c: Unknown result type (might be due to invalid IL or missing references)
//IL_0181: Unknown result type (might be due to invalid IL or missing references)
//IL_018b: Expected O, but got Unknown
//IL_0294: Unknown result type (might be due to invalid IL or missing references)
//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
//IL_030c: Unknown result type (might be due to invalid IL or missing references)
Rows.Clear();
button = null;
panel = null;
title = null;
open = false;
if (ArtifactRegistry.All.Count == 0 || !UiReflection.TryInit())
{
return;
}
Transform parent = ((Component)settings).transform.parent;
Transform val = settings.startButton.transform.parent.Find("BackBtn");
UiSettings friendlyFireSetting = settings.friendlyFireSetting;
Transform list = ((Component)friendlyFireSetting).transform.parent;
int siblingIndex = ((Component)friendlyFireSetting).transform.GetSiblingIndex();
if ((Object)(object)val == (Object)null || siblingIndex < 1 || siblingIndex + 1 >= list.childCount || (Object)(object)UiReflection.FindText(((Component)list.GetChild(siblingIndex - 1)).gameObject) == (Object)null)
{
ArtifactLobbyPlugin.LogOnce("build", "Artifact Lobby: the lobby's Back button or Player Damage row is not where expected; no Artifacts panel.");
return;
}
GameObject gameObject = ((Component)list.GetChild(siblingIndex - 1)).gameObject;
GameObject gameObject2 = ((Component)list.GetChild(siblingIndex + 1)).gameObject;
GameObject val2 = null;
GameObject val3 = null;
try
{
val2 = CloneInactive(((Component)val.parent).gameObject, parent, "ArtifactLobbyButton", delegate(GameObject clone)
{
DestroyChildren(clone.transform);
});
Place(val2, new Vector2(-50f, -642.6f), new Vector2(350f, 75f));
GameObject obj = Object.Instantiate<GameObject>(((Component)val).gameObject, val2.transform, false);
UiReflection.ReplaceOnClick(obj, new UnityAction(Toggle));
UiReflection.SetText(UiReflection.FindText(obj), "Artifacts");
Transform newList = null;
val3 = CloneInactive(((Component)settings).gameObject, parent, "ArtifactLobbyPanel", delegate(GameObject clone)
{
Object.DestroyImmediate((Object)(object)clone.GetComponent<LobbySettings>());
newList = clone.transform.Find(((Object)list).name);
DestroyChildren(newList);
});
Component val4 = UiReflection.FindText(val3);
List<Row> list2 = new List<Row>();
foreach (ArtifactEntry item in ArtifactRegistry.All)
{
if (list2.Count > 0)
{
Clone(gameObject2, newList, 5f);
}
UiReflection.SetText(UiReflection.FindText(Clone(gameObject, newList, 30f)), item.Name);
GameObject obj2 = Clone(((Component)friendlyFireSetting).gameObject, newList, 68.6f);
DestroyChildren(obj2.transform);
UiSettings component = obj2.GetComponent<UiSettings>();
component.AddSettings(item.IsOn ? 1 : 0, OffOn);
Component val5 = UiReflection.FindText(Clone(gameObject, newList, 28f));
UiReflection.SetAutoSize(val5, 12f, 22f);
UiReflection.SetColor(val5, DescriptionColor);
UiReflection.SetText(val5, item.Description);
list2.Add(new Row
{
Entry = item,
Setting = component,
Description = val5
});
}
Place(val3, new Vector2(-420f, -50f), new Vector2(350f, PanelLayout.Height(list2.Count)));
title = val4;
button = val2;
panel = val3;
Rows.AddRange(list2);
}
catch
{
if ((Object)(object)val2 != (Object)null)
{
Object.Destroy((Object)(object)val2);
}
if ((Object)(object)val3 != (Object)null)
{
Object.Destroy((Object)(object)val3);
}
throw;
}
LogPositions();
Sync();
}
public static void Sync()
{
if (!Built)
{
return;
}
PanelView panelView = CurrentView();
if (!panelView.Visible)
{
open = false;
}
button.SetActive(panelView.Visible);
panel.SetActive(open);
if (!panelView.Visible)
{
return;
}
UiReflection.SetText(title, panelView.Title);
for (int i = 0; i < Rows.Count; i++)
{
int num = (panelView.Rows[i].On ? 1 : 0);
if (Rows[i].Setting.setting != num)
{
UiReflection.UpdateSetting(Rows[i].Setting, num);
}
UiReflection.SetText(Rows[i].Description, panelView.Rows[i].Description);
}
}
public static void OnSettingChanged(UiSettings setting)
{
if (!Built)
{
return;
}
int num = Rows.FindIndex((Row row) => row.Setting == setting);
if (num < 0)
{
return;
}
bool flag = setting.setting == 1;
if (LobbySession.IsHost)
{
ArtifactEntry entry = Rows[num].Entry;
if (entry.IsOn != flag)
{
entry.Set(flag);
LobbySession.Publish();
ArtifactLobbyPlugin.Log.LogInfo((object)("Artifact Lobby: host turned " + entry.Name + " " + (flag ? "on" : "off")));
}
}
else
{
PanelView panelView = CurrentView();
if (panelView.Visible && panelView.Rows[num].On != flag)
{
UiReflection.UpdateSetting(setting, panelView.Rows[num].On ? 1 : 0);
}
}
}
private static void Toggle()
{
if (Built)
{
open = !open;
ArtifactLobbyPlugin.Log.LogInfo((object)(open ? "Artifact Lobby: panel opened" : "Artifact Lobby: panel closed"));
Sync();
if (open)
{
LogPositions();
}
}
}
private static PanelView CurrentView()
{
return PanelView.For(LobbySession.IsHost, LobbySession.HostPicks, Rows.ConvertAll((Row row) => row.Entry));
}
private static GameObject CloneInactive(GameObject source, Transform parent, string name, Action<GameObject> strip)
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Expected O, but got Unknown
GameObject val = new GameObject("ArtifactLobbyHolder");
val.SetActive(false);
try
{
GameObject val2 = Object.Instantiate<GameObject>(source, val.transform, false);
((Object)val2).name = name;
strip(val2);
val2.transform.SetParent(parent, false);
return val2;
}
finally
{
Object.DestroyImmediate((Object)(object)val);
}
}
private static GameObject Clone(GameObject source, Transform parent, float height)
{
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: 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)
GameObject obj = Object.Instantiate<GameObject>(source, parent, false);
RectTransform val = (RectTransform)obj.transform;
val.sizeDelta = new Vector2(val.sizeDelta.x, height);
return obj;
}
private static void DestroyChildren(Transform parent)
{
for (int num = parent.childCount - 1; num >= 0; num--)
{
Object.DestroyImmediate((Object)(object)((Component)parent.GetChild(num)).gameObject);
}
}
private static void Place(GameObject target, Vector2 position, Vector2 size)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: 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: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
RectTransform val = (RectTransform)target.transform;
Vector2 val2 = default(Vector2);
((Vector2)(ref val2))..ctor(1f, 1f);
val.pivot = val2;
Vector2 anchorMin = (val.anchorMax = val2);
val.anchorMin = anchorMin;
val.anchoredPosition = position;
val.sizeDelta = size;
}
private static void LogPositions()
{
Canvas.ForceUpdateCanvases();
ArtifactLobbyPlugin.Log.LogInfo((object)("Artifact Lobby: Artifacts button at " + Centre(button.transform)));
foreach (Row row in Rows)
{
Transform transform = ((Component)row.Setting).transform;
ArtifactLobbyPlugin.Log.LogInfo((object)("Artifact Lobby: " + row.Entry.Name + " Off at " + Centre(transform.GetChild(0)) + ", On at " + Centre(transform.GetChild(1))));
}
if (!PanelLayout.FitsOnScreen(Rows.Count))
{
ArtifactLobbyPlugin.Log.LogWarning((object)string.Format(CultureInfo.InvariantCulture, "Artifact Lobby: {0} artifacts make the panel {1:0.#} tall; its bottom runs off a 1080-high screen.", Rows.Count, PanelLayout.Height(Rows.Count)));
}
}
private static string Centre(Transform target)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: 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_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
Vector3[] array = (Vector3[])(object)new Vector3[4];
((RectTransform)target).GetWorldCorners(array);
Vector3 val = (array[0] + array[2]) * 0.5f;
return string.Format(CultureInfo.InvariantCulture, "{0:0.000},{1:0.000}", val.x / (float)Screen.width, 1f - val.y / (float)Screen.height);
}
}
public sealed class ArtifactEntry
{
private readonly Func<bool> isOn;
private readonly Action<bool> setOn;
public string Id { get; }
public string Name { get; }
public string Description { get; }
public bool IsOn
{
get
{
try
{
return isOn();
}
catch (Exception arg)
{
ArtifactLobbyPlugin.LogOnce("get:" + Id, $"Artifact Lobby: reading {Name}'s switch failed: {arg}");
return false;
}
}
}
internal ArtifactEntry(string id, string name, string description, Func<bool> isOn, Action<bool> setOn)
{
Id = id;
Name = name;
Description = description;
this.isOn = isOn;
this.setOn = setOn;
}
internal void Set(bool on)
{
if (IsOn == on)
{
return;
}
try
{
setOn(on);
}
catch (Exception arg)
{
ArtifactLobbyPlugin.LogOnce("set:" + Id, string.Format("Artifact Lobby: turning {0} {1} failed: {2}", Name, on ? "on" : "off", arg));
}
}
}
public static class ArtifactRegistry
{
private static readonly List<ArtifactEntry> Entries = new List<ArtifactEntry>();
internal static IReadOnlyList<ArtifactEntry> All => Entries;
public static bool Register(string id, string name, string description, ConfigEntry<bool> enabled)
{
if (enabled == null)
{
return Refuse(id, "no Enabled config entry");
}
return Register(id, name, description, () => enabled.Value, delegate(bool on)
{
enabled.Value = on;
if (!((ConfigEntryBase)enabled).ConfigFile.SaveOnConfigSet)
{
((ConfigEntryBase)enabled).ConfigFile.Save();
}
});
}
public static bool Register(string id, string name, string description, Func<bool> isOn, Action<bool> setOn)
{
string text = Problem(id, isOn, setOn);
if (text != null)
{
return Refuse(id, text);
}
Entries.RemoveAll((ArtifactEntry entry) => entry.Id == id);
Entries.Add(new ArtifactEntry(id, string.IsNullOrEmpty(name) ? id : name, description ?? "", isOn, setOn));
Entries.Sort(delegate(ArtifactEntry a, ArtifactEntry b)
{
int num = string.CompareOrdinal(a.Name, b.Name);
return (num == 0) ? string.CompareOrdinal(a.Id, b.Id) : num;
});
return true;
}
internal static string Summary()
{
if (Entries.Count == 0)
{
return "No artifact mods registered, so no lobby panel. In a lobby you host, players running Artifact Lobby get every artifact turned off.";
}
List<string> list = new List<string>();
foreach (ArtifactEntry entry in Entries)
{
list.Add(entry.Name);
}
string arg = ((Entries.Count == 1) ? "artifact" : "artifacts");
return string.Format("{0} {1} registered: {2}", Entries.Count, arg, string.Join(", ", list));
}
internal static void Reset()
{
Entries.Clear();
}
private static bool Refuse(string id, string problem)
{
ManualLogSource log = ArtifactLobbyPlugin.Log;
if (log != null)
{
log.LogWarning((object)("Artifact Lobby: refused to register '" + id + "': " + problem + "."));
}
return false;
}
private static string Problem(string id, Func<bool> isOn, Action<bool> setOn)
{
if (isOn == null || setOn == null)
{
return "no on/off getter and setter";
}
if (string.IsNullOrEmpty(id))
{
return "empty id";
}
if (id.IndexOfAny(ArtifactWire.Reserved) >= 0)
{
return "the id holds one of | , =";
}
return null;
}
}
internal static class ArtifactWire
{
public const string LobbyKey = "evan.muck.artifactlobby";
private const string Version = "1";
internal static readonly char[] Reserved = new char[3] { '|', ',', '=' };
public static string Encode(IEnumerable<(string Id, bool On)> picks)
{
StringBuilder stringBuilder = new StringBuilder("1").Append('|');
bool flag = true;
foreach (var (value, flag2) in picks)
{
if (!flag)
{
stringBuilder.Append(',');
}
stringBuilder.Append(value).Append('=').Append(flag2 ? '1' : '0');
flag = false;
}
return stringBuilder.ToString();
}
public static bool TryDecode(string text, out Dictionary<string, bool> picks)
{
picks = null;
if (string.IsNullOrEmpty(text))
{
return false;
}
int num = text.IndexOf('|');
if (num < 0 || text.Substring(0, num) != "1")
{
return false;
}
Dictionary<string, bool> dictionary = new Dictionary<string, bool>();
string text2 = text.Substring(num + 1);
if (text2.Length > 0)
{
string[] array = text2.Split(new char[1] { ',' });
for (int i = 0; i < array.Length; i++)
{
string[] array2 = array[i].Split(new char[1] { '=' });
if (array2.Length != 2 || array2[0].Length == 0 || array2[0].IndexOf('|') >= 0)
{
return false;
}
if (array2[1] != "0" && array2[1] != "1")
{
return false;
}
if (dictionary.ContainsKey(array2[0]))
{
return false;
}
dictionary[array2[0]] = array2[1] == "1";
}
}
picks = dictionary;
return true;
}
}
internal enum PickReason
{
HostOn,
HostOff,
HostLacks
}
internal readonly struct ArtifactPick
{
public string Id { get; }
public PickReason Reason { get; }
public bool On => Reason == PickReason.HostOn;
public ArtifactPick(string id, PickReason reason)
{
Id = id;
Reason = reason;
}
}
internal sealed class HostPlan
{
public IReadOnlyList<ArtifactPick> Picks { get; }
public IReadOnlyList<string> MissingHere { get; }
private HostPlan(IReadOnlyList<ArtifactPick> picks, IReadOnlyList<string> missingHere)
{
Picks = picks;
MissingHere = missingHere;
}
public static HostPlan For(IEnumerable<string> localIds, IReadOnlyDictionary<string, bool> host)
{
List<string> local = localIds.ToList();
bool value;
List<ArtifactPick> picks = local.Select((string id) => new ArtifactPick(id, (!host.TryGetValue(id, out value)) ? PickReason.HostLacks : ((!value) ? PickReason.HostOff : PickReason.HostOn))).ToList();
List<string> missingHere = (from pair in host
where pair.Value && !local.Contains(pair.Key)
select pair.Key).OrderBy<string, string>((string id) => id, StringComparer.Ordinal).ToList();
return new HostPlan(picks, missingHere);
}
public void ApplyTo(IEnumerable<ArtifactEntry> entries)
{
Dictionary<string, bool> dictionary = Picks.ToDictionary((ArtifactPick pick) => pick.Id, (ArtifactPick pick) => pick.On);
foreach (ArtifactEntry entry in entries)
{
if (dictionary.TryGetValue(entry.Id, out var value))
{
entry.Set(value);
}
}
}
public string Describe(Func<string, string> nameOf)
{
if (Picks.Count == 0)
{
return "none";
}
return string.Join(", ", Picks.Select((ArtifactPick pick) => nameOf(pick.Id) + Word(pick.Reason)));
}
private static string Word(PickReason reason)
{
return reason switch
{
PickReason.HostOn => " on",
PickReason.HostOff => " off",
_ => " off (host lacks it)",
};
}
}
internal enum LobbyRole
{
Idle,
Host,
Client
}
internal static class LobbyRoles
{
public static LobbyRole For(bool ownsLobby, int localArtifacts)
{
if (!ownsLobby)
{
if (localArtifacts <= 0)
{
return LobbyRole.Idle;
}
return LobbyRole.Client;
}
return LobbyRole.Host;
}
}
internal static class PanelLayout
{
public const float LabelHeight = 30f;
public const float RowHeight = 68.6f;
public const float DescriptionHeight = 28f;
public const float LineHeight = 5f;
public const float MaxHeight = 1030f;
private const float Chrome = 70f;
private const float ListPadding = 35f;
private const float Gap = 8f;
public static float Height(int artifacts)
{
int num = 4 * artifacts - 1;
return 105f + (float)artifacts * 126.6f + (float)(artifacts - 1) * 5f + (float)(num - 1) * 8f;
}
public static bool FitsOnScreen(int artifacts)
{
return Height(artifacts) <= 1030f;
}
}
internal readonly struct RowView
{
public bool On { get; }
public string Description { get; }
public RowView(bool on, string description)
{
On = on;
Description = description;
}
}
internal sealed class PanelView
{
public const string HostTitle = "Artifacts";
public const string ClientTitle = "Host's artifacts";
public const string HostLacksText = "The host does not have this artifact.";
private static readonly PanelView Hidden = new PanelView(null, new RowView[0]);
public string Title { get; }
public bool Visible => Title != null;
public IReadOnlyList<RowView> Rows { get; }
private PanelView(string title, IReadOnlyList<RowView> rows)
{
Title = title;
Rows = rows;
}
public static PanelView For(bool isHost, IReadOnlyDictionary<string, bool> hostPicks, IReadOnlyList<ArtifactEntry> entries)
{
if (isHost)
{
return new PanelView("Artifacts", entries.Select((ArtifactEntry entry) => new RowView(entry.IsOn, entry.Description)).ToList());
}
if (hostPicks == null)
{
return Hidden;
}
HostPlan plan = HostPlan.For(entries.Select((ArtifactEntry entry) => entry.Id), hostPicks);
return new PanelView("Host's artifacts", entries.Select((ArtifactEntry entry, int i) => new RowView(plan.Picks[i].On, (plan.Picks[i].Reason == PickReason.HostLacks) ? "The host does not have this artifact." : entry.Description)).ToList());
}
}
[HarmonyPatch(typeof(LobbyVisuals), "OpenLobby")]
internal static class LobbyOpenPatch
{
private static void Postfix(LobbyVisuals __instance)
{
try
{
LobbySession.Open(SteamLobbyData.CurrentLobby(__instance));
}
catch (Exception arg)
{
LobbySession.Clear();
ArtifactLobbyPlugin.LogOnce("LobbyOpenPatch", $"Artifact Lobby: lobby open failed: {arg}");
}
}
}
[HarmonyPatch(typeof(LobbyVisuals), "CloseLobby", new Type[] { })]
internal static class LobbyClosePatch
{
private static void Postfix()
{
try
{
LobbySession.Clear();
}
catch (Exception arg)
{
ArtifactLobbyPlugin.LogOnce("LobbyClosePatch", $"Artifact Lobby: lobby close failed: {arg}");
}
}
}
[HarmonyPatch(typeof(ClientHandle), "StartGame", new Type[] { typeof(Packet) })]
internal static class StartGamePatch
{
private static void Prefix()
{
try
{
LobbySession.OnStartGame();
}
catch (Exception arg)
{
ArtifactLobbyPlugin.LogOnce("StartGamePatch", $"Artifact Lobby: applying the host's artifacts failed: {arg}");
}
}
}
[HarmonyPatch(typeof(LobbySettings), "Start", new Type[] { })]
internal static class LobbySettingsStartPatch
{
private static void Postfix(LobbySettings __instance)
{
try
{
ArtifactPanel.Build(__instance);
}
catch (Exception arg)
{
ArtifactLobbyPlugin.LogOnce("LobbySettingsStartPatch", $"Artifact Lobby: building the Artifacts panel failed: {arg}");
}
}
}
[HarmonyPatch(typeof(UiSettings), "UpdateSetting", new Type[] { typeof(int) })]
internal static class UpdateSettingPatch
{
private static void Postfix(UiSettings __instance)
{
try
{
ArtifactPanel.OnSettingChanged(__instance);
}
catch (Exception arg)
{
ArtifactLobbyPlugin.LogOnce("UpdateSettingPatch", $"Artifact Lobby: an artifact click failed: {arg}");
}
}
}
internal static class LobbySession
{
private const float PollSeconds = 0.5f;
private static object lobby;
private static float nextPoll;
private static string hostText;
public static bool IsHost { get; private set; }
public static Dictionary<string, bool> HostPicks { get; private set; }
public static event Action Changed;
public static void Open(object steamLobby)
{
Reset();
LobbyRole lobbyRole = LobbyRoles.For(SteamLobbyData.OwnerId(steamLobby) == SteamLobbyData.LocalSteamId(), ArtifactRegistry.All.Count);
if (lobbyRole != LobbyRole.Idle)
{
lobby = steamLobby;
IsHost = lobbyRole == LobbyRole.Host;
if (IsHost)
{
Publish();
}
else
{
Refresh();
}
}
LobbySession.Changed?.Invoke();
}
public static void Publish()
{
if (lobby != null && IsHost)
{
SteamLobbyData.SetData(lobby, "evan.muck.artifactlobby", ArtifactWire.Encode(ArtifactRegistry.All.Select((ArtifactEntry entry) => (Id: entry.Id, IsOn: entry.IsOn))));
}
}
public static void Poll(float now)
{
if (lobby != null && !IsHost && !(now < nextPoll))
{
nextPoll = now + 0.5f;
Refresh();
}
}
public static void Clear()
{
Reset();
LobbySession.Changed?.Invoke();
}
private static void Reset()
{
lobby = null;
IsHost = false;
nextPoll = 0f;
hostText = null;
HostPicks = null;
}
public static void OnStartGame()
{
if (lobby == null)
{
return;
}
IReadOnlyList<ArtifactEntry> all = ArtifactRegistry.All;
Dictionary<string, string> names = all.ToDictionary((ArtifactEntry entry) => entry.Id, (ArtifactEntry entry) => entry.Name);
IEnumerable<string> localIds = all.Select((ArtifactEntry entry) => entry.Id);
if (IsHost)
{
Publish();
Dictionary<string, bool> host = all.ToDictionary((ArtifactEntry entry) => entry.Id, (ArtifactEntry entry) => entry.IsOn);
ArtifactLobbyPlugin.Log.LogInfo((object)("Artifact Lobby: host artifacts: " + HostPlan.For(localIds, host).Describe((string id) => names[id])));
return;
}
Refresh();
if (HostPicks == null)
{
ArtifactLobbyPlugin.Log.LogInfo((object)(string.IsNullOrEmpty(hostText) ? "Artifact Lobby: the host does not run Artifact Lobby; your own artifact configs apply." : ("Artifact Lobby: could not read the host's artifacts (" + hostText + "); your own artifact configs apply.")));
return;
}
HostPlan hostPlan = HostPlan.For(localIds, HostPicks);
hostPlan.ApplyTo(all);
ArtifactLobbyPlugin.Log.LogInfo((object)("Artifact Lobby: applied the host's artifacts: " + hostPlan.Describe((string id) => names[id])));
if (hostPlan.MissingHere.Count > 0)
{
ArtifactLobbyPlugin.Log.LogWarning((object)("Artifact Lobby: the host has artifacts on that you do not have installed: " + string.Join(", ", hostPlan.MissingHere)));
}
}
private static void Refresh()
{
string data = SteamLobbyData.GetData(lobby, "evan.muck.artifactlobby");
if (!(data == hostText))
{
hostText = data;
HostPicks = (ArtifactWire.TryDecode(data, out var picks) ? picks : null);
LobbySession.Changed?.Invoke();
}
}
}
internal static class SteamLobbyData
{
private const BindingFlags InstanceMembers = BindingFlags.Instance | BindingFlags.Public;
private static FieldInfo currentLobby;
private static MethodInfo setData;
private static MethodInfo getData;
private static PropertyInfo owner;
private static PropertyInfo localSteamId;
private static Func<object, object> friendId;
private static Func<object, object> steamIdValue;
public static bool TryInit()
{
List<string> list = new List<string>();
try
{
currentLobby = typeof(LobbyVisuals).GetField("currentLobby", BindingFlags.Instance | BindingFlags.NonPublic);
Type type = currentLobby?.FieldType;
setData = type?.GetMethod("SetData", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
{
typeof(string),
typeof(string)
}, null);
getData = type?.GetMethod("GetData", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
owner = type?.GetProperty("Owner", BindingFlags.Instance | BindingFlags.Public);
friendId = Getter(owner?.PropertyType, "Id");
localSteamId = type?.Assembly.GetType("Steamworks.SteamClient")?.GetProperty("SteamId", BindingFlags.Static | BindingFlags.Public);
steamIdValue = Getter(localSteamId?.PropertyType, "Value");
if (currentLobby == null)
{
list.Add("LobbyVisuals.currentLobby");
}
if (setData == null || setData.ReturnType != typeof(bool))
{
list.Add("Lobby.SetData(string, string)");
}
if (getData == null || getData.ReturnType != typeof(string))
{
list.Add("Lobby.GetData(string)");
}
if (owner == null)
{
list.Add("Lobby.Owner");
}
if (friendId == null)
{
list.Add("Friend.Id");
}
if (localSteamId == null)
{
list.Add("SteamClient.SteamId");
}
if (steamIdValue == null)
{
list.Add("SteamId.Value");
}
}
catch (Exception ex)
{
list.Add(ex.ToString());
}
if (list.Count == 0)
{
return true;
}
ManualLogSource log = ArtifactLobbyPlugin.Log;
if (log != null)
{
log.LogError((object)("Artifact Lobby: could not find " + string.Join(", ", list) + ". Lobby sync is off; each player's own artifact configs apply."));
}
return false;
}
public static object CurrentLobby(LobbyVisuals lobbyVisuals)
{
return currentLobby.GetValue(lobbyVisuals);
}
public static void SetData(object lobby, string key, string value)
{
setData.Invoke(lobby, new object[2] { key, value });
}
public static string GetData(object lobby, string key)
{
return (getData.Invoke(lobby, new object[1] { key }) as string) ?? "";
}
public static ulong OwnerId(object lobby)
{
return FriendId(owner.GetValue(lobby, null));
}
public static ulong LocalSteamId()
{
return SteamIdValue(localSteamId.GetValue(null, null));
}
internal static ulong FriendId(object friend)
{
return SteamIdValue(friendId(friend));
}
private static ulong SteamIdValue(object steamId)
{
return Convert.ToUInt64(steamIdValue(steamId));
}
private static Func<object, object> Getter(Type type, string name)
{
PropertyInfo property = type?.GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
if (property != null)
{
return (object target) => property.GetValue(target, null);
}
FieldInfo field = type?.GetField(name, BindingFlags.Instance | BindingFlags.Public);
if (field != null)
{
return (object target) => field.GetValue(target);
}
return null;
}
}
internal static class UiReflection
{
private const BindingFlags Public = BindingFlags.Instance | BindingFlags.Public;
private static bool? ready;
private static Type tmpText;
private static Type button;
private static PropertyInfo text;
private static PropertyInfo fontSize;
private static PropertyInfo enableAutoSizing;
private static PropertyInfo fontSizeMin;
private static PropertyInfo fontSizeMax;
private static PropertyInfo color;
private static PropertyInfo onClick;
private static MethodInfo updateSetting;
public static bool TryInit()
{
if (ready.HasValue)
{
return ready.Value;
}
List<string> list = new List<string>();
try
{
tmpText = Type.GetType("TMPro.TMP_Text, Unity.TextMeshPro");
button = Type.GetType("UnityEngine.UI.Button, UnityEngine.UI");
if (tmpText == null)
{
list.Add("TMPro.TMP_Text");
}
if (button == null)
{
list.Add("UnityEngine.UI.Button");
}
text = Settable(tmpText, "text", typeof(string), list);
fontSize = Settable(tmpText, "fontSize", typeof(float), list);
enableAutoSizing = Settable(tmpText, "enableAutoSizing", typeof(bool), list);
fontSizeMin = Settable(tmpText, "fontSizeMin", typeof(float), list);
fontSizeMax = Settable(tmpText, "fontSizeMax", typeof(float), list);
color = Settable(tmpText, "color", typeof(Color), list);
onClick = button?.GetProperty("onClick", BindingFlags.Instance | BindingFlags.Public);
bool flag = onClick != null && onClick.CanWrite && typeof(UnityEvent).IsAssignableFrom(onClick.PropertyType) && onClick.PropertyType.GetConstructor(Type.EmptyTypes) != null;
if (button != null && !flag)
{
list.Add("Button.onClick");
}
updateSetting = typeof(UiSettings).GetMethod("UpdateSetting", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null);
if (updateSetting == null)
{
list.Add("UiSettings.UpdateSetting(int)");
}
}
catch (Exception ex)
{
list.Add(ex.ToString());
}
ready = list.Count == 0;
if (!ready.Value)
{
ManualLogSource log = ArtifactLobbyPlugin.Log;
if (log != null)
{
log.LogError((object)("Artifact Lobby: could not find " + string.Join(", ", list) + ". The lobby panel is off; lobby sync still runs."));
}
}
return ready.Value;
}
public static Component FindText(GameObject root)
{
return root.GetComponentInChildren(tmpText, true);
}
public static void SetText(Component tmp, string value)
{
text.SetValue(tmp, value, null);
}
public static void SetColor(Component tmp, Color value)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
color.SetValue(tmp, value, null);
}
public static void SetAutoSize(Component tmp, float min, float max)
{
enableAutoSizing.SetValue(tmp, true, null);
fontSizeMin.SetValue(tmp, min, null);
fontSizeMax.SetValue(tmp, max, null);
fontSize.SetValue(tmp, max, null);
}
public static void ReplaceOnClick(GameObject buttonObject, UnityAction action)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Expected O, but got Unknown
UnityEvent val = (UnityEvent)Activator.CreateInstance(onClick.PropertyType);
val.AddListener(action);
onClick.SetValue(buttonObject.GetComponent(button), val, null);
}
public static void UpdateSetting(UiSettings row, int value)
{
updateSetting.Invoke(row, new object[1] { value });
}
private static PropertyInfo Settable(Type type, string name, Type valueType, List<string> missing)
{
PropertyInfo propertyInfo = null;
try
{
propertyInfo = type?.GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
}
catch (AmbiguousMatchException)
{
}
if (propertyInfo != null && propertyInfo.CanWrite && propertyInfo.PropertyType == valueType)
{
return propertyInfo;
}
if (type != null)
{
missing.Add("TMP_Text." + name);
}
return null;
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "MuckArtifactLobby";
public const string PLUGIN_NAME = "Artifact Lobby";
public const string PLUGIN_VERSION = "0.1.0";
}
}