Due to update 2.4.3, some mods may no longer function. FixedConfig may be necessary.
Decompiled source of BoplModSyncer v1.0.0
BoplModSyncer.dll
Decompiled 8 months agousing System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BoplModSyncer.Utils; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using Steamworks.Data; using TMPro; using TinyJson; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.6", FrameworkDisplayName = ".NET Framework 4.6")] [assembly: AssemblyCompany("BoplModSyncer")] [assembly: AssemblyConfiguration("Thunderstore")] [assembly: AssemblyDescription("Checks if all connected players have necessary mods")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+6b35826ca40c07445c3c7a81ed56537f4e736479")] [assembly: AssemblyProduct("BoplModSyncer")] [assembly: AssemblyTitle("BoplModSyncer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [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 TinyJson { public static class JSONParser { [ThreadStatic] private static Stack<List<string>> splitArrayPool; [ThreadStatic] private static StringBuilder stringBuilder; [ThreadStatic] private static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfoCache; [ThreadStatic] private static Dictionary<Type, Dictionary<string, PropertyInfo>> propertyInfoCache; public static T FromJson<T>(this string json) { if (propertyInfoCache == null) { propertyInfoCache = new Dictionary<Type, Dictionary<string, PropertyInfo>>(); } if (fieldInfoCache == null) { fieldInfoCache = new Dictionary<Type, Dictionary<string, FieldInfo>>(); } if (stringBuilder == null) { stringBuilder = new StringBuilder(); } if (splitArrayPool == null) { splitArrayPool = new Stack<List<string>>(); } stringBuilder.Length = 0; for (int i = 0; i < json.Length; i++) { char c = json[i]; if (c == '"') { i = AppendUntilStringEnd(appendEscapeCharacter: true, i, json); } else if (!char.IsWhiteSpace(c)) { stringBuilder.Append(c); } } return (T)ParseValue(typeof(T), stringBuilder.ToString()); } private static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json) { stringBuilder.Append(json[startIdx]); for (int i = startIdx + 1; i < json.Length; i++) { if (json[i] == '\\') { if (appendEscapeCharacter) { stringBuilder.Append(json[i]); } stringBuilder.Append(json[i + 1]); i++; } else { if (json[i] == '"') { stringBuilder.Append(json[i]); return i; } stringBuilder.Append(json[i]); } } return json.Length - 1; } private static List<string> Split(string json) { List<string> list = ((splitArrayPool.Count > 0) ? splitArrayPool.Pop() : new List<string>()); list.Clear(); if (json.Length == 2) { return list; } int num = 0; stringBuilder.Length = 0; for (int i = 1; i < json.Length - 1; i++) { switch (json[i]) { case '[': case '{': num++; break; case ']': case '}': num--; break; case '"': i = AppendUntilStringEnd(appendEscapeCharacter: true, i, json); continue; case ',': case ':': if (num == 0) { list.Add(stringBuilder.ToString()); stringBuilder.Length = 0; continue; } break; } stringBuilder.Append(json[i]); } list.Add(stringBuilder.ToString()); return list; } internal static object ParseValue(Type type, string json) { if (type == typeof(string)) { if (json.Length <= 2) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(json.Length); for (int i = 1; i < json.Length - 1; i++) { if (json[i] == '\\' && i + 1 < json.Length - 1) { int num = "\"\\nrtbf/".IndexOf(json[i + 1]); if (num >= 0) { stringBuilder.Append("\"\\\n\r\t\b\f/"[num]); i++; continue; } if (json[i + 1] == 'u' && i + 5 < json.Length - 1 && uint.TryParse(json.Substring(i + 2, 4), NumberStyles.AllowHexSpecifier, null, out var result)) { stringBuilder.Append((char)result); i += 5; continue; } } stringBuilder.Append(json[i]); } return stringBuilder.ToString(); } if (type.IsPrimitive) { return Convert.ChangeType(json, type, CultureInfo.InvariantCulture); } if (type == typeof(decimal)) { decimal.TryParse(json, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2); return result2; } if (type == typeof(DateTime)) { DateTime.TryParse(json.Replace("\"", ""), CultureInfo.InvariantCulture, DateTimeStyles.None, out var result3); return result3; } if (json == "null") { return null; } if (type.IsEnum) { if (json[0] == '"') { json = json.Substring(1, json.Length - 2); } try { return Enum.Parse(type, json, ignoreCase: false); } catch { return 0; } } if (type.IsArray) { Type elementType = type.GetElementType(); if (json[0] != '[' || json[json.Length - 1] != ']') { return null; } List<string> list = Split(json); Array array = Array.CreateInstance(elementType, list.Count); for (int j = 0; j < list.Count; j++) { array.SetValue(ParseValue(elementType, list[j]), j); } splitArrayPool.Push(list); return array; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { Type type2 = type.GetGenericArguments()[0]; if (json[0] != '[' || json[json.Length - 1] != ']') { return null; } List<string> list2 = Split(json); IList list3 = (IList)type.GetConstructor(new Type[1] { typeof(int) }).Invoke(new object[1] { list2.Count }); for (int k = 0; k < list2.Count; k++) { list3.Add(ParseValue(type2, list2[k])); } splitArrayPool.Push(list2); return list3; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { Type[] genericArguments = type.GetGenericArguments(); Type type3 = genericArguments[0]; Type type4 = genericArguments[1]; if (type3 != typeof(string)) { return null; } if (json[0] != '{' || json[json.Length - 1] != '}') { return null; } List<string> list4 = Split(json); if (list4.Count % 2 != 0) { return null; } IDictionary dictionary = (IDictionary)type.GetConstructor(new Type[1] { typeof(int) }).Invoke(new object[1] { list4.Count / 2 }); for (int l = 0; l < list4.Count; l += 2) { if (list4[l].Length > 2) { string key = list4[l].Substring(1, list4[l].Length - 2); object value = ParseValue(type4, list4[l + 1]); dictionary[key] = value; } } return dictionary; } if (type == typeof(object)) { return ParseAnonymousValue(json); } if (json[0] == '{' && json[json.Length - 1] == '}') { return ParseObject(type, json); } return null; } private static object ParseAnonymousValue(string json) { if (json.Length == 0) { return null; } if (json[0] == '{' && json[json.Length - 1] == '}') { List<string> list = Split(json); if (list.Count % 2 != 0) { return null; } Dictionary<string, object> dictionary = new Dictionary<string, object>(list.Count / 2); for (int i = 0; i < list.Count; i += 2) { dictionary[list[i].Substring(1, list[i].Length - 2)] = ParseAnonymousValue(list[i + 1]); } return dictionary; } if (json[0] == '[' && json[json.Length - 1] == ']') { List<string> list2 = Split(json); List<object> list3 = new List<object>(list2.Count); for (int j = 0; j < list2.Count; j++) { list3.Add(ParseAnonymousValue(list2[j])); } return list3; } if (json[0] == '"' && json[json.Length - 1] == '"') { string text = json.Substring(1, json.Length - 2); return text.Replace("\\", string.Empty); } if (char.IsDigit(json[0]) || json[0] == '-') { if (json.Contains(".")) { double.TryParse(json, NumberStyles.Float, CultureInfo.InvariantCulture, out var result); return result; } int.TryParse(json, out var result2); return result2; } if (json == "true") { return true; } if (json == "false") { return false; } return null; } private static Dictionary<string, T> CreateMemberNameDictionary<T>(T[] members) where T : MemberInfo { Dictionary<string, T> dictionary = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase); foreach (T val in members) { if (val.IsDefined(typeof(IgnoreDataMemberAttribute), inherit: true)) { continue; } string name = val.Name; if (val.IsDefined(typeof(DataMemberAttribute), inherit: true)) { DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(val, typeof(DataMemberAttribute), inherit: true); if (!string.IsNullOrEmpty(dataMemberAttribute.Name)) { name = dataMemberAttribute.Name; } } dictionary.Add(name, val); } return dictionary; } private static object ParseObject(Type type, string json) { object uninitializedObject = FormatterServices.GetUninitializedObject(type); List<string> list = Split(json); if (list.Count % 2 != 0) { return uninitializedObject; } if (!fieldInfoCache.TryGetValue(type, out var value)) { value = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)); fieldInfoCache.Add(type, value); } if (!propertyInfoCache.TryGetValue(type, out var value2)) { value2 = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)); propertyInfoCache.Add(type, value2); } for (int i = 0; i < list.Count; i += 2) { if (list[i].Length > 2) { string key = list[i].Substring(1, list[i].Length - 2); string json2 = list[i + 1]; PropertyInfo value4; if (value.TryGetValue(key, out var value3)) { value3.SetValue(uninitializedObject, ParseValue(value3.FieldType, json2)); } else if (value2.TryGetValue(key, out value4)) { value4.SetValue(uninitializedObject, ParseValue(value4.PropertyType, json2), null); } } } return uninitializedObject; } } public static class JSONWriter { public static string ToJson(this object item) { StringBuilder stringBuilder = new StringBuilder(); AppendValue(stringBuilder, item); return stringBuilder.ToString(); } private static void AppendValue(StringBuilder stringBuilder, object item) { if (item == null) { stringBuilder.Append("null"); return; } Type type = item.GetType(); if (type == typeof(string) || type == typeof(char)) { stringBuilder.Append('"'); string text = item.ToString(); for (int i = 0; i < text.Length; i++) { if (text[i] < ' ' || text[i] == '"' || text[i] == '\\') { stringBuilder.Append('\\'); int num = "\"\\\n\r\t\b\f".IndexOf(text[i]); if (num >= 0) { stringBuilder.Append("\"\\nrtbf"[num]); } else { stringBuilder.AppendFormat("u{0:X4}", (uint)text[i]); } } else { stringBuilder.Append(text[i]); } } stringBuilder.Append('"'); } else if (type == typeof(byte) || type == typeof(sbyte)) { stringBuilder.Append(item.ToString()); } else if (type == typeof(short) || type == typeof(ushort)) { stringBuilder.Append(item.ToString()); } else if (type == typeof(int) || type == typeof(uint)) { stringBuilder.Append(item.ToString()); } else if (type == typeof(long) || type == typeof(ulong)) { stringBuilder.Append(item.ToString()); } else if (type == typeof(float)) { stringBuilder.Append(((float)item).ToString(CultureInfo.InvariantCulture)); } else if (type == typeof(double)) { stringBuilder.Append(((double)item).ToString(CultureInfo.InvariantCulture)); } else if (type == typeof(decimal)) { stringBuilder.Append(((decimal)item).ToString(CultureInfo.InvariantCulture)); } else if (type == typeof(bool)) { stringBuilder.Append(((bool)item) ? "true" : "false"); } else if (type == typeof(DateTime)) { stringBuilder.Append('"'); stringBuilder.Append(((DateTime)item).ToString(CultureInfo.InvariantCulture)); stringBuilder.Append('"'); } else if (type.IsEnum) { stringBuilder.Append('"'); stringBuilder.Append(item.ToString()); stringBuilder.Append('"'); } else if (item is IList) { stringBuilder.Append('['); bool flag = true; IList list = item as IList; for (int j = 0; j < list.Count; j++) { if (flag) { flag = false; } else { stringBuilder.Append(','); } AppendValue(stringBuilder, list[j]); } stringBuilder.Append(']'); } else { if (!type.IsGenericType || !(type.GetGenericTypeDefinition() == typeof(Dictionary<, >))) { return; } Type type2 = type.GetGenericArguments()[0]; if (type2 != typeof(string)) { stringBuilder.Append("{}"); return; } stringBuilder.Append('{'); IDictionary dictionary = item as IDictionary; bool flag2 = true; foreach (object key in dictionary.Keys) { if (flag2) { flag2 = false; } else { stringBuilder.Append(','); } stringBuilder.Append('"'); stringBuilder.Append((string)key); stringBuilder.Append("\":"); AppendValue(stringBuilder, dictionary[key]); } stringBuilder.Append('}'); } } } } namespace BoplModSyncer { internal static class Patches { private static readonly string checksumField = GameUtils.GenerateField("checksum"); private static readonly string hostModListField = GameUtils.GenerateField("modlist"); private static readonly string hostConfigListField = GameUtils.GenerateField("configlist"); private static readonly string memberHasSyncerField = GameUtils.GenerateField("syncer"); private static bool firstJoin = true; private static bool hostSetupDone = false; private static bool isHost = false; public static void OnEnterLobby_Prefix(Lobby lobby) { //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) hostSetupDone = (isHost = false); if (SteamManager.LocalPlayerIsLobbyOwner) { if (firstJoin && Plugin.lastLobbyId.Value != 0) { Plugin.logger.LogMessage((object)("rejoining: " + Plugin.lastLobbyId.Value)); new Traverse((object)SteamManager.instance).Method("TryJoinLobby", new object[1] { Plugin.lastLobbyId.Value }).GetValue(); } else { OnHostJoin(lobby); } return; } ((Lobby)(ref lobby)).SetMemberData(memberHasSyncerField, "1"); string data = ((Lobby)(ref lobby)).GetData(checksumField); firstJoin = false; Plugin.lastLobbyId.Value = 0uL; if (data == Plugin.CHECKSUM) { SyncConfigs(lobby); } else if (string.IsNullOrEmpty(data)) { HostDoesntHaveSyncer(lobby); } else { ModMismatch(lobby); } } private static void LeaveLobby(string message = null) { SteamManager.instance.LeaveLobby(); Plugin.logger.LogWarning((object)("Left lobby because: '" + message + "'")); } private static void HostDoesntHaveSyncer(Lobby lobby) { LeaveLobby("host doesnt have syncer"); PanelMaker.InstantiatePanel(Plugin.noSyncerPanel); } private static void InstallMods(OnlineModData[] toInstallMods, LocalModData[] toDeleteMods) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) Queue<OnlineModData> modsToDownload = new Queue<OnlineModData>(); for (int i = 0; i < toInstallMods.Length; i++) { OnlineModData item = toInstallMods[i]; if (item.Link != "" && item.DoInstall) { modsToDownload.Enqueue(item); } } GameObject installingPanel = PanelMaker.InstantiatePanel(Plugin.installingPanel); Slider progressBar = PanelMaker.GetProgressBarComp(); TextMeshProUGUI percentageText = ((Component)installingPanel.transform.Find("ProgressBar/Percentage")).GetComponent<TextMeshProUGUI>(); TextMeshProUGUI infoText = PanelMaker.GetInfoText(); TextMeshProUGUI textArea = PanelMaker.GetTextAreaText(); Button okButton = PanelMaker.GetOkButtonComp(); ((Selectable)okButton).interactable = false; ((Graphic)textArea).color = Color.red; Directory.CreateDirectory(GameUtils.DownloadedModsPath); downloadNext(); void downloadComplete() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown ((TMP_Text)infoText).text = "Press OK to restart game"; ((TMP_Text)PanelMaker.GetTitleText()).text = "Downloading completed!"; ((Selectable)okButton).interactable = true; ((UnityEvent)okButton.onClick).AddListener((UnityAction)delegate { GameUtils.RestartGameAfterDownload(toDeleteMods); }); } void downloadNext() { if (!modsToDownload.Any()) { downloadComplete(); } else { OnlineModData downloadingMod = modsToDownload.Dequeue(); WebClient client = new WebClient(); client.OpenReadCompleted += async delegate(object sender, OpenReadCompletedEventArgs args) { try { if (args.Cancelled) { throw new Exception("cancelled"); } if (args.Error != null) { throw args.Error; } string[] linkParts = downloadingMod.Link.Split(new char[1] { '/' }); string name = string.Join("-", linkParts[5], linkParts[6], linkParts[7]) + ".zip"; string path = Path.Combine(GameUtils.DownloadedModsPath, name); Plugin.logger.LogInfo((object)("downloading: " + name)); ((TMP_Text)infoText).text = downloadingMod.Guid + " v" + downloadingMod.Version; using FileStream file = File.Create(path); await BaseUtils.CopyToAsync(args.Result, file, new SynchronousProgress<int>(delegate(int value) { progressBar.value = value; ((TMP_Text)percentageText).text = $"{value}%"; })); downloadNext(); } catch (Exception ex2) { Exception ex = ex2; Plugin.logger.LogError((object)$"error while downloading: {ex}"); ((TMP_Text)textArea).text = ex.Message; Button continueButton = ((Component)installingPanel.transform.Find("Continue Button")).GetComponent<Button>(); ((Component)continueButton).gameObject.SetActive(true); ((UnityEvent)continueButton.onClick).AddListener((UnityAction)delegate { ((Component)continueButton).gameObject.SetActive(false); ((TMP_Text)textArea).text = ""; downloadNext(); }); } finally { client.Dispose(); } }; client.OpenReadAsync(new Uri(downloadingMod.Link)); } } } private static void ModMismatch(Lobby lobby) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Expected O, but got Unknown string hostModListText = ((Lobby)(ref lobby)).GetData(hostModListField); string data = ((Lobby)(ref lobby)).GetData(hostConfigListField); Plugin.lastLobbyId.Value = SteamId.op_Implicit(((Lobby)(ref lobby)).Id); LeaveLobby("Missing mods"); MethodInfo methodInfo = null; MethodInfo[] methods = typeof(ConfigFile).GetMethods(); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name != "Bind")) { ParameterInfo[] parameters = methodInfo2.GetParameters(); if (parameters[0].ParameterType == typeof(ConfigDefinition)) { methodInfo = methodInfo2; break; } } } if (methodInfo == null) { throw new NullReferenceException("No bind method was found"); } string[] array = hostModListText.Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); OnlineModData[] toInstallMods = new OnlineModData[array.Length]; int num = 0; Dictionary<string, HostConfigEntry[]> hostConfigs = GameUtils.GetHostConfigs(data); string[] array2 = array; foreach (string text in array2) { string[] array3 = text.Split(new char[1] { ',' }); OnlineModData onlineModData = new OnlineModData(array3[0], array3[1], array3[2], array3[3]); if (hostConfigs.TryGetValue(onlineModData.Guid, out var value)) { ConfigFile val = new ConfigFile(Path.Combine(Paths.ConfigPath, onlineModData.Guid + ".cfg"), true); HostConfigEntry[] array4 = value; for (int k = 0; k < array4.Length; k++) { HostConfigEntry hostConfigEntry = array4[k]; object obj = ((!(hostConfigEntry.Type == typeof(string))) ? ((!hostConfigEntry.Type.IsValueType) ? null : Activator.CreateInstance(hostConfigEntry.Type)) : ""); object obj2 = methodInfo.MakeGenericMethod(hostConfigEntry.Type).Invoke(val, new object[3] { hostConfigEntry.Definition, obj, null }); ((ConfigEntryBase)((obj2 is ConfigEntryBase) ? obj2 : null)).SetSerializedValue(hostConfigEntry.Value); } val.Save(); } if (!Plugin.mods.TryGetValue(onlineModData.Guid, out var value2) || !(value2.Hash == onlineModData.Hash)) { toInstallMods[num] = onlineModData; num++; } } Array.Resize(ref toInstallMods, num); LocalModData[] toDeleteMods = Plugin.mods.Values.Where((LocalModData e) => !hostModListText.Contains(e.Hash)).ToArray(); Plugin.logger.LogWarning((object)("missing:\n\t- " + string.Join("\n\t- ", toInstallMods.Select((OnlineModData m) => m.Guid + " v" + m.Version)))); Plugin.logger.LogWarning((object)("to delete:\n\t- " + string.Join("\n\t- ", toDeleteMods.Select((LocalModData m) => $"{m.Plugin.Metadata.GUID} {m.Plugin.Metadata.Version}")))); GameObject val2 = PanelMaker.InstantiatePanel(Plugin.missingModsPanel, (UnityAction)delegate { InstallMods(toInstallMods, toDeleteMods); }); Transform row = val2.transform.Find("NeededModList/Viewport/Content/tmprow"); LinkClicker linkClicker = val2.AddComponent<LinkClicker>(); for (int l = 0; l < toInstallMods.Length; l++) { makeInstallRow(l); } for (int n = 0; n < toDeleteMods.Length; n++) { makeDeleteRow(n); } void makeDeleteRow(int index) { makeRow(index, toDelete: true); } void makeInstallRow(int index) { makeRow(index, toDelete: false); } void makeRow(int index, bool toDelete) { //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) IModData modData2; if (!toDelete) { IModData modData = toInstallMods[index]; modData2 = modData; } else { IModData modData = toDeleteMods[index]; modData2 = modData; } IModData modData3 = modData2; Transform val3 = Object.Instantiate<Transform>(row, row.parent); ((Component)val3).gameObject.SetActive(true); TextMeshProUGUI component = ((Component)val3.Find("Background/Label")).GetComponent<TextMeshProUGUI>(); Toggle component2 = ((Component)val3).GetComponent<Toggle>(); StringBuilder stringBuilder = new StringBuilder(modData3.Guid).Append(" v").Append(modData3.Version).Append(" (") .Append(modData3.Hash.Substring(0, 4)) .Append(")"); if (!string.IsNullOrEmpty(modData3.Link)) { string[] array5 = modData3.Link.Split(new char[1] { '/' }); string value3 = "https://thunderstore.io/c/bopl-battle/p/" + array5[5] + "/" + array5[6] + "/"; stringBuilder.Append(" <link=").Append(value3).Append("><color=blue><b>link</b></color></link>"); } ((TMP_Text)component).text = stringBuilder.ToString(); linkClicker.textMeshes.Add(component); if (toDelete) { ((TMP_Text)component).fontStyle = (FontStyles)(((TMP_Text)component).fontStyle | 0x40); ((UnityEvent<bool>)(object)component2.onValueChanged).AddListener((UnityAction<bool>)delegate(bool isOn) { toDeleteMods[index].DoDelete = isOn; }); component2.isOn = true; } else if (!string.IsNullOrEmpty(modData3.Link)) { ((UnityEvent<bool>)(object)component2.onValueChanged).AddListener((UnityAction<bool>)delegate(bool isOn) { toInstallMods[index].DoInstall = isOn; }); component2.isOn = true; } else { ((Selectable)component2).interactable = false; } } } private static void SyncConfigs(Lobby lobby) { //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Expected O, but got Unknown //IL_011c: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, HostConfigEntry[]> hostConfigs = GameUtils.GetHostConfigs(((Lobby)(ref lobby)).GetData(hostConfigListField)); Dictionary<string, List<KeyValuePair<ConfigEntryBase, string>>> newConfigs = new Dictionary<string, List<KeyValuePair<ConfigEntryBase, string>>>(); bool flag = true; foreach (KeyValuePair<string, LocalModData> mod in Plugin.mods) { ConfigFile config = mod.Value.Plugin.Instance.Config; List<KeyValuePair<ConfigEntryBase, string>> list = new List<KeyValuePair<ConfigEntryBase, string>>(); if (!hostConfigs.TryGetValue(mod.Key, out var value)) { continue; } foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config) { ConfigEntryBase value2 = item.Value; string text = ""; HostConfigEntry[] array = value; for (int i = 0; i < array.Length; i++) { HostConfigEntry hostConfigEntry = array[i]; if (hostConfigEntry.Definition.Equals(value2.Definition)) { text = hostConfigEntry.Value; break; } } if (flag && text != value2.GetSerializedValue()) { flag = false; Plugin.lastLobbyId.Value = SteamId.op_Implicit(((Lobby)(ref lobby)).Id); LeaveLobby("syncing configs"); } if (!flag) { list.Add(new KeyValuePair<ConfigEntryBase, string>(value2, text)); } } if (!flag) { newConfigs.Add(mod.Key, list); } } if (flag) { return; } GameObject val = PanelMaker.InstantiatePanel(Plugin.restartPanel, (UnityAction)delegate { Directory.CreateDirectory(GameUtils.OldConfigsPath); foreach (KeyValuePair<string, LocalModData> mod2 in Plugin.mods) { ConfigFile config2 = mod2.Value.Plugin.Instance.Config; string destFileName = Path.Combine(GameUtils.OldConfigsPath, Path.GetFileName(config2.ConfigFilePath)); try { File.Copy(config2.ConfigFilePath, destFileName); } catch (IOException) { } config2.SaveOnConfigSet = false; foreach (KeyValuePair<ConfigEntryBase, string> item2 in newConfigs[mod2.Key]) { item2.Key.SetSerializedValue(item2.Value); } config2.Save(); } GameUtils.RestartGameAfterSync(); }); } private static void OnHostJoin(Lobby lobby) { isHost = true; ((Lobby)(ref lobby)).SetData(checksumField, Plugin.CHECKSUM); Dictionary<string, List<string[]>> dictionary = new Dictionary<string, List<string[]>>(); StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, LocalModData> mod in Plugin.mods) { LocalModData value = mod.Value; ConfigFile config = value.Plugin.Instance.Config; stringBuilder.Append(mod.Key).Append(',').Append(value.Version) .Append(',') .Append(value.Link) .Append(',') .Append(value.Hash) .Append('|'); if (config.Count == 0) { continue; } List<string[]> list = new List<string[]>(); foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config) { ConfigEntryBase value2 = item.Value; string text = value2.SettingType.FullName; if (Type.GetType(text) == null) { text = value2.SettingType.AssemblyQualifiedName; } list.Add(new string[3] { item.Key.MyToString(), text, value2.GetSerializedValue() }); } dictionary.Add(mod.Key, list); } if (stringBuilder.Length > 0) { stringBuilder.RemoveLast(); } ((Lobby)(ref lobby)).SetData(hostModListField, stringBuilder.ToString()); ((Lobby)(ref lobby)).SetData(hostConfigListField, dictionary.ToJson()); hostSetupDone = true; } internal static void OnLobbyMemberJoinedCallback_Postfix(Lobby lobby, Friend friend) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //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) if (!isHost) { return; } if (!hostSetupDone) { int num = SteamManager.instance.connectedPlayers.FindIndex((SteamConnection e) => SteamId.op_Implicit(e.id) == SteamId.op_Implicit(friend.Id)); if (num != -1) { SteamManager.instance.KickPlayer(num); Plugin.logger.LogWarning((object)("Kicked \"" + ((Friend)(ref friend)).Name + "\" because host(you) still havent finished booting!")); } } else { ((MonoBehaviour)Plugin.plugin).StartCoroutine(waitForField()); } IEnumerator waitForField() { yield return (object)new WaitForSeconds(1f); if (!(((Lobby)(ref lobby)).GetMemberData(friend, memberHasSyncerField) == "1")) { int playerIndex = SteamManager.instance.connectedPlayers.FindIndex((SteamConnection e) => SteamId.op_Implicit(e.id) == SteamId.op_Implicit(friend.Id)); if (playerIndex != -1) { SteamManager.instance.KickPlayer(playerIndex); Plugin.logger.LogWarning((object)("Kicked \"" + ((Friend)(ref friend)).Name + "\" because he doesnt has syncer!")); } } } } internal static void JoinLobby_Prefix() { if ((Object)(object)PanelMaker.currentPanel != (Object)null) { ((UnityEvent)PanelMaker.GetCancelButtonComp().onClick).Invoke(); } } } [BepInPlugin("com.almafa64.BoplModSyncer", "BoplModSyncer", "1.0.0")] [BepInProcess("BoplBattle.exe")] public class Plugin : BaseUnityPlugin { internal const string THUNDERSTORE_BOPL_MODS = "https://thunderstore.io/c/bopl-battle/api/v1/package"; internal const string GITHUB_CLIENT_ONLY_GUIDS = "https://raw.githubusercontent.com/almafa64/almafa64-bopl-mods/dev/BoplModSyncer/client_only_guids.txt"; private static readonly Dictionary<string, LocalModData> _mods = new Dictionary<string, LocalModData>(); public static readonly ReadOnlyDictionary<string, LocalModData> mods = new ReadOnlyDictionary<string, LocalModData>(_mods); public static readonly bool IsDemo = Path.GetFileName(Paths.GameRootPath) == "Bopl Battle Demo"; internal static Harmony harmony; internal static ManualLogSource logger; internal static ConfigFile config; internal static Plugin plugin; internal static ConfigEntry<ulong> lastLobbyId; internal static string _checksum; internal static readonly HashSet<string> _clientOnlyGuids = new HashSet<string>(); internal static GameObject genericPanel; internal static GameObject missingModsPanel; internal static GameObject noSyncerPanel; internal static GameObject installingPanel; internal static GameObject restartPanel; private static GameObject checksumTextObj; public static string CHECKSUM => _checksum ?? throw new Exception("CHECKSUM hasn't been calculated"); private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Expected O, but got Unknown harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); logger = ((BaseUnityPlugin)this).Logger; config = ((BaseUnityPlugin)this).Config; plugin = this; WebClient webClient = new WebClient(); string[] array = webClient.DownloadString("https://raw.githubusercontent.com/almafa64/almafa64-bopl-mods/dev/BoplModSyncer/client_only_guids.txt").Split(new char[1] { '\n' }); foreach (string text in array) { _clientOnlyGuids.Add(text.Trim()); } lastLobbyId = config.Bind<ulong>("BoplModSyncer", "last lobby id", 0uL, (ConfigDescription)null); SceneManager.sceneLoaded += OnSceneLoaded; harmony.Patch((MethodBase)AccessTools.Method(typeof(SteamManager), "OnLobbyEnteredCallback", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Patches), "OnEnterLobby_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method(typeof(SteamManager), "OnLobbyMemberJoinedCallback", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Patches), "OnLobbyMemberJoinedCallback_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)AccessTools.Method("Steamworks.ISteamMatchmaking:JoinLobby", new Type[1] { typeof(SteamId) }, (Type[])null), new HarmonyMethod(typeof(Patches), "JoinLobby_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); AssetBundle val = AssetBundle.LoadFromStream(BaseUtils.GetResourceStream("BoplModSyncer", "PanelBundle")); genericPanel = val.LoadAsset<GameObject>("GenericPanel"); val.Unload(false); GameUtils.Init(); Directory.CreateDirectory(GameUtils.MyCachePath); } private void Start() { WebClient webClient = new WebClient(); List<object> list = (List<object>)webClient.DownloadString("https://thunderstore.io/c/bopl-battle/api/v1/package").FromJson<object>(); Dictionary<string, Dictionary<string, string>> dictionary = new Dictionary<string, Dictionary<string, string>>(); foreach (object item in list) { Dictionary<string, object> dictionary2 = item as Dictionary<string, object>; Dictionary<string, string> dictionary3 = new Dictionary<string, string>(); foreach (object item2 in (List<object>)dictionary2["versions"]) { Dictionary<string, object> dictionary4 = (Dictionary<string, object>)item2; dictionary3.Add((string)dictionary4["version_number"], (string)dictionary4["download_url"]); } dictionary.Add((string)dictionary2["full_name"], dictionary3); } List<string> list2 = new List<string>(); foreach (PluginInfo value2 in Chainloader.PluginInfos.Values) { if (!_clientOnlyGuids.Contains(value2.Metadata.GUID)) { string text = BaseUtils.ChecksumFile(value2.Location); logger.LogInfo((object)(value2.Metadata.GUID + " - " + text)); list2.Add(text); Manifest manifest = GameUtils.GetManifest(value2); string fileName = Path.GetFileName(manifest?.Directory); string text2 = fileName?.Substring(0, fileName.LastIndexOf('-')); string link = ((text2 == null) ? "" : GeneralExtensions.GetValueSafe<string, string>(GeneralExtensions.GetValueSafe<string, Dictionary<string, string>>(dictionary, text2), manifest.Version)); LocalModData localModData = new LocalModData(link); localModData.Manifest = manifest; localModData.Plugin = value2; localModData.Hash = text; LocalModData value = localModData; _mods.Add(value2.Metadata.GUID, value); } } MakeChecksumText(BaseUtils.CombineHashes(list2)); PanelMaker.MakeGenericPanel(ref genericPanel); noSyncerPanel = PanelMaker.MakeNoSyncerPanel(genericPanel); missingModsPanel = PanelMaker.MakeMissingModsPanel(genericPanel); installingPanel = PanelMaker.MakeInstallingPanel(genericPanel); restartPanel = PanelMaker.MakeRestartPanel(genericPanel); Object.Destroy((Object)(object)genericPanel); genericPanel = null; } private void MakeChecksumText(string checksum) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (_checksum != null) { throw new Exception("Checksum text was already made!"); } _checksum = checksum; Transform canvas = PanelUtils.GetCanvas(); GameObject val = GameObject.Find("ExitText"); GameObject val2 = Object.Instantiate<GameObject>(val, canvas); ((Object)val2).name = "hashText"; Object.Destroy((Object)(object)val2.GetComponent<LocalizedText>()); TextMeshProUGUI component = val2.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).transform.position = val.transform.position; ((TMP_Text)component).text = checksum; checksumTextObj = Object.Instantiate<GameObject>(val2); ((Object)checksumTextObj).hideFlags = (HideFlags)1; Object.DontDestroyOnLoad((Object)(object)checksumTextObj); Object.Destroy((Object)(object)val2); ShowChecksumText(10); } private void ShowChecksumText(int ypos) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)checksumTextObj == (Object)null)) { GameObject val = Object.Instantiate<GameObject>(checksumTextObj, PanelUtils.GetCanvas()); Vector3 val2 = Camera.main.WorldToScreenPoint(val.transform.position); val2.y = ypos; val.transform.position = Camera.main.ScreenToWorldPoint(val2); } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (((Scene)(ref scene)).name == "MainMenu") { OnMainMenuloaded(); } else if (((Scene)(ref scene)).name.Contains("Select")) { OnSelectMenuLoaded(); } } private void OnMainMenuloaded() { ShowChecksumText(10); } private void OnSelectMenuLoaded() { ShowChecksumText(1500); } } public interface IModData { string Guid { get; } string Version { get; } string Link { get; } string Hash { get; } } public struct OnlineModData : IModData { public string Guid { get; internal set; } public string Version { get; internal set; } public string Link { get; internal set; } public string Hash { get; internal set; } public bool DoInstall { get; internal set; } public OnlineModData(string guid, string version, string link, string hash) { Guid = guid; Version = version; Link = link; Hash = hash; DoInstall = false; } public override readonly string ToString() { return "version: '" + Version + "', link: '" + Link + "', guid: '" + Guid + "', hash: '" + Hash + "'"; } } public struct LocalModData : IModData { private PluginInfo _plugin; private Manifest _manifest; public string Guid { get; private set; } public string Version { get; private set; } public string Hash { get; internal set; } public string Link { get; internal set; } public bool DoDelete { get; internal set; } public PluginInfo Plugin { readonly get { return _plugin; } internal set { if (_plugin != null) { throw new Exception("Plugin was already set!"); } _plugin = value; PluginInfo plugin = _plugin; Guid = ((plugin != null) ? plugin.Metadata.GUID : null); if (Version == null) { PluginInfo plugin2 = _plugin; string text2 = (Version = ((plugin2 != null) ? plugin2.Metadata.Version.ToString() : null)); } } } public Manifest Manifest { readonly get { return _manifest; } internal set { if (_manifest != null) { throw new Exception("Manifest was already set!"); } _manifest = value; if (_manifest != null) { Version = _manifest.Version; } } } public LocalModData(string link) { Guid = null; Version = null; Hash = null; _plugin = null; _manifest = null; Link = link; DoDelete = false; } public override readonly string ToString() { return "name: '" + Plugin.Metadata.Name + "', version: '" + Version + "', link: '" + Link + "', guid: '" + Guid + "', hash: '" + Hash + "'"; } } public struct HostConfigEntry { internal Type Type { get; private set; } internal string Value { get; private set; } internal ConfigDefinition Definition { get; private set; } public HostConfigEntry(ConfigDefinition definition, Type type, string value) { Type = type; Value = value; Definition = definition; } } public class Manifest { public string Name { get; private set; } public string Version { get; private set; } public string Website { get; private set; } public string Description { get; private set; } public ReadOnlyCollection<string> Dependencies { get; private set; } public string Directory { get; private set; } internal Manifest(ManifestJSON json, string dir) { Name = json.name; Version = json.version_number; Website = json.website_url; Description = json.description; Dependencies = new ReadOnlyCollection<string>(json.dependencies); Directory = dir; } } internal class ManifestJSON { public string name = null; public string version_number = null; public string website_url = null; public string description = null; public string[] dependencies = null; } public static class TypeExnteders { public static StringBuilder RemoveLast(this StringBuilder sb) { return sb.Remove(sb.Length - 1, 1); } public static string MyToString(this ConfigDefinition def) { return def.Section + "=" + def.Key; } } internal class LinkClicker : MonoBehaviour, IPointerClickHandler, IEventSystemHandler { public List<TextMeshProUGUI> textMeshes = new List<TextMeshProUGUI>(); public void OnPointerClick(PointerEventData eventData) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) foreach (TextMeshProUGUI textMesh in textMeshes) { int num = TMP_TextUtilities.FindIntersectingLink((TMP_Text)(object)textMesh, Vector2.op_Implicit(eventData.position), Camera.current); if (num == -1) { continue; } TMP_LinkInfo val = ((TMP_Text)textMesh).textInfo.linkInfo[num]; Application.OpenURL(((TMP_LinkInfo)(ref val)).GetLinkID()); break; } } } public static class PluginInfo { public const string PLUGIN_GUID = "BoplModSyncer"; public const string PLUGIN_NAME = "BoplModSyncer"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace BoplModSyncer.Utils { public class BaseUtils { public static string BytesToString(byte[] bytes) { return BitConverter.ToString(bytes).Replace("-", ""); } public static string ChecksumFile(string path) { using SHA256 sHA = SHA256.Create(); using FileStream fileStream = File.OpenRead(path); byte[] bytes = sHA.ComputeHash(fileStream); return BytesToString(bytes) + fileStream.Length; } public static string CombineHashes(List<string> hashes) { StringBuilder stringBuilder = new StringBuilder(); foreach (string hash in hashes) { stringBuilder.Append(hash); } using SHA256 sHA = SHA256.Create(); return BytesToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(stringBuilder.ToString()))); } public static async Task CopyToAsync(Stream source, Stream destination, IProgress<int> progress, int bufferSize = 4096, CancellationToken cancellationToken = default(CancellationToken)) { byte[] buffer = new byte[bufferSize]; long totalBytesRead = 0L; while (true) { int num; int bytesRead = (num = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken)); if (num <= 0) { break; } await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); totalBytesRead += bytesRead; progress.Report(Convert.ToInt32(totalBytesRead * 100 / source.Length)); } } public static Stream GetResourceStream(string namespaceName, string path) { return Assembly.GetExecutingAssembly().GetManifestResourceStream(namespaceName + "." + path); } public static string GetRelativePath(string path, string relativeDirectory) { return path.Substring(path.IndexOf(relativeDirectory) + relativeDirectory.Length); } } public sealed class SynchronousProgress<T> : IProgress<T> { private readonly Action<T> _callback; public SynchronousProgress(Action<T> callback) { _callback = callback; base..ctor(); } void IProgress<T>.Report(T data) { _callback(data); } } internal static class GameUtils { private const string DataPrefix = "almafa64>"; public static string MyCachePath { get; private set; } public static string DownloadedModsPath { get; private set; } public static string OldConfigsPath { get; private set; } public static string OldPluginPath { get; private set; } internal static void Init() { MyCachePath = Path.Combine(Paths.CachePath, ((BaseUnityPlugin)Plugin.plugin).Info.Metadata.GUID); DownloadedModsPath = Path.Combine(MyCachePath, "downloaded"); OldConfigsPath = Path.Combine(MyCachePath, "configs_old"); OldPluginPath = Path.Combine(MyCachePath, "plugins_old"); } public static void CancelSyncing(WebClient client = null) { Plugin.lastLobbyId.Value = 0uL; client?.CancelAsync(); try { Directory.Delete(DownloadedModsPath, recursive: true); } catch (DirectoryNotFoundException) { } } public static void RestartGameAfterDownload(LocalModData[] toDeleteMods) { int num = (Plugin.IsDemo ? 2494960 : 1686940); int id = Process.GetCurrentProcess().Id; string gUID = ((BaseUnityPlugin)Plugin.plugin).Info.Metadata.GUID; string text = Path.Combine(MyCachePath, "mod_installer_deleter.bat"); string fileName = Path.GetFileName(MyCachePath); string fileName2 = Path.GetFileName(DownloadedModsPath); string fileName3 = Path.GetFileName(OldPluginPath); string text2 = ";"; if (toDeleteMods != null && toDeleteMods.Length != 0) { text2 = "\"" + string.Join("\";\"", toDeleteMods.Select((LocalModData e) => BaseUtils.GetRelativePath(e.Plugin.Location, Paths.PluginPath + "\\"))) + "\""; } File.WriteAllText(text, $"@echo off\r\ncd BepInEx\\cache\\{Path.GetFileName(MyCachePath)} 2>nul\r\ngoto :start\r\n\r\n:err_check\r\nif not \"%errorlevel%\" == \"0\" (\r\n\techo\\\r\n\techo \u001b[91mthere was an error code: %errorlevel%!\u001b[0m\r\n\tpause\r\n\texit 0\r\n)\r\nexit /b 0\r\n\r\nrem wait for game to finish\r\n:start\r\ntasklist /fi \"pid eq {id}\" /fo csv 2>nul | find /i \"{id}\" > nul\r\nif \"%ERRORLEVEL%\"==\"0\" goto start\r\nset \"errorlevel=0\"\r\n\r\ncall :err_check\r\n\r\nrem only copy mods if there is no copy already\r\nif not exist \"{fileName3}\" (\r\n\techo\\\r\n\techo \u001b[92mcopying old mods to new folder\u001b[0m\r\n\trobocopy ..\\..\\plugins\\ {fileName3}\\ /e /ndl /nc /ns\r\n\tcall :err_check\r\n)\r\n\r\necho\\\r\necho \u001b[92mdeleting not needed mods\u001b[0m\r\nfor %%f in ({text2}) do del ..\\..\\plugins\\%%f\r\n\r\ncall :err_check\r\n\r\necho\\\r\necho \u001b[92mcopying downloaded dlls into mod folder\u001b[0m\r\ncd ..\\..\\plugins\r\nfor /r %%f in (..\\cache\\{fileName}\\{fileName2}\\*.zip) do (\r\n\tmd \"%%~nf\"\r\n\tcd \"%%~nf\"\r\n\ttar -xvf \"%%f\"\r\n\tcd ..\r\n)\r\ncd ..\\cache\\{Path.GetFileName(MyCachePath)}\r\n\r\ncall :err_check\r\n\r\necho\\\r\necho \u001b[92mcleanup\u001b[0m\r\nrmdir /s /q \"{fileName2}\"\r\n\r\ncall :err_check\r\n\r\necho\\\r\necho \u001b[91mRestarting game!\u001b[0m\r\nstart \"\" steam://rungameid/{num}"); Process.Start(text); Application.Quit(); } public static void RestartGameAfterSync() { int num = (Plugin.IsDemo ? 2494960 : 1686940); int id = Process.GetCurrentProcess().Id; string text = Path.Combine(MyCachePath, "syncer.bat"); File.WriteAllText(text, $"@echo off\r\n\r\nrem wait for game to finish\r\n:start\r\ntasklist /fi \"pid eq {id}\" /fo csv 2>nul | find /i \"{id}\" > nul\r\nif \"%ERRORLEVEL%\"==\"0\" goto start\r\nset \"errorlevel=0\"\r\n\r\ntimeout 2\r\n\r\necho\\\r\necho \u001b[91mRestarting game!\u001b[0m\r\nstart \"\" steam://rungameid/{num}"); Process.Start(text); Application.Quit(); } public static string GenerateField(string key) { return "almafa64>" + key; } public static Manifest GetManifest(PluginInfo plugin) { string text = plugin.Location; while ((text = Path.GetDirectoryName(text)).Length > Paths.PluginPath.Length) { string path = Path.Combine(text, "manifest.json"); try { string text2 = File.ReadAllText(path); return new Manifest(JsonUtility.FromJson<ManifestJSON>(text2), text); } catch (FileNotFoundException) { } catch (Exception ex2) { Plugin.logger.LogError((object)ex2); break; } } return null; } public static Dictionary<string, HostConfigEntry[]> GetHostConfigs(string hostConfigListText) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown Dictionary<string, HostConfigEntry[]> dictionary = new Dictionary<string, HostConfigEntry[]>(); Dictionary<string, List<string[]>> dictionary2 = hostConfigListText.FromJson<Dictionary<string, List<string[]>>>(); foreach (KeyValuePair<string, List<string[]>> item in dictionary2) { List<string[]> value = item.Value; HostConfigEntry[] array = new HostConfigEntry[value.Count]; for (int i = 0; i < value.Count; i++) { string[] array2 = value[i]; string[] array3 = array2[0].Split(new char[1] { '=' }, 2); array[i] = new HostConfigEntry(new ConfigDefinition(array3[0], array3[1]), Type.GetType(array2[1]), array2[2]); } dictionary.Add(item.Key, array); } return dictionary; } } internal class PanelMaker { internal static GameObject currentPanel; private static MainMenu currentMenu; public static GameObject GetTitle(GameObject panel = null) { return ((Component)GetPanel(panel).Find("Title")).gameObject; } public static GameObject GetInfo(GameObject panel = null) { return ((Component)GetPanel(panel).Find("Info")).gameObject; } public static GameObject GetTextArea(GameObject panel = null) { return ((Component)GetPanel(panel).Find("Text")).gameObject; } public static GameObject GetProgressBar(GameObject panel = null) { return ((Component)GetPanel(panel).Find("ProgressBar")).gameObject; } public static GameObject GetModList(GameObject panel = null) { return ((Component)GetPanel(panel).Find("NeededModList")).gameObject; } public static GameObject GetOkButton(GameObject panel = null) { return ((Component)GetPanel(panel).Find("OK Button")).gameObject; } public static GameObject GetCancelButton(GameObject panel = null) { return ((Component)GetPanel(panel).Find("Cancel Button")).gameObject; } public static Slider GetProgressBarComp(GameObject panel = null) { return GetProgressBar(panel).GetComponent<Slider>(); } public static TextMeshProUGUI GetTitleText(GameObject panel = null) { return GetTitle(panel).GetComponent<TextMeshProUGUI>(); } public static TextMeshProUGUI GetInfoText(GameObject panel = null) { return GetInfo(panel).GetComponent<TextMeshProUGUI>(); } public static TextMeshProUGUI GetTextAreaText(GameObject panel = null) { return GetTextArea(panel).GetComponent<TextMeshProUGUI>(); } public static Button GetOkButtonComp(GameObject panel = null) { return GetOkButton(panel).GetComponent<Button>(); } public static Button GetCancelButtonComp(GameObject panel = null) { return GetCancelButton(panel).GetComponent<Button>(); } private static Transform GetPanel(GameObject panel) { return (panel ?? currentPanel ?? throw new NullReferenceException("panel parameter and current panel is null")).transform; } private static GameObject CopyGeneric(GameObject genericPanel, string name) { GameObject val = Object.Instantiate<GameObject>(genericPanel); ((Object)val).hideFlags = (HideFlags)1; Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).name = name; return val; } private static void SetupButtons(GameObject panel, UnityAction onOkClick) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown ((UnityEvent)GetOkButtonComp(panel).onClick).AddListener((UnityAction)delegate { close(); UnityAction obj = onOkClick; if (obj != null) { obj.Invoke(); } }); ((UnityEvent)GetCancelButtonComp(panel).onClick).AddListener((UnityAction)delegate { GameUtils.CancelSyncing(); close(); }); void close() { Object.Destroy((Object)(object)panel); currentPanel = null; currentMenu.EnableAll(); } } private static TMP_FontAsset GetDefaultFont() { return LocalizedText.localizationTable.GetFont((Language)0, false); } public static void MakeGenericPanel(ref GameObject genericPanel) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: 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_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) TextMeshProUGUI val = GetTitle(genericPanel).AddComponent<TextMeshProUGUI>(); TextMeshProUGUI val2 = GetInfo(genericPanel).gameObject.AddComponent<TextMeshProUGUI>(); TextMeshProUGUI val3 = ((Component)genericPanel.transform.Find("OK Button/Text (TMP)")).gameObject.AddComponent<TextMeshProUGUI>(); TextMeshProUGUI val4 = ((Component)genericPanel.transform.Find("Cancel Button/Text (TMP)")).gameObject.AddComponent<TextMeshProUGUI>(); TextMeshProUGUI val5 = GetTextArea(genericPanel).gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val).fontSize = 56f; ((Graphic)val).color = Color.black; ((TMP_Text)val).font = GetDefaultFont(); ((TMP_Text)val).alignment = (TextAlignmentOptions)513; ((TMP_Text)val).fontStyle = (FontStyles)1; float fontSize = (((TMP_Text)val3).fontSize = 50f); ((TMP_Text)val4).fontSize = fontSize; Color color = (((Graphic)val3).color = Color.black); ((Graphic)val4).color = color; TMP_FontAsset font = (((TMP_Text)val3).font = GetDefaultFont()); ((TMP_Text)val4).font = font; TextAlignmentOptions alignment = (TextAlignmentOptions)514; ((TMP_Text)val3).alignment = (TextAlignmentOptions)514; ((TMP_Text)val4).alignment = alignment; FontStyles fontStyle = (FontStyles)1; ((TMP_Text)val3).fontStyle = (FontStyles)1; ((TMP_Text)val4).fontStyle = fontStyle; ((TMP_Text)val3).text = "OK"; ((TMP_Text)val4).text = "Cancel"; ((TMP_Text)val2).fontSize = 50f; ((Graphic)val2).color = Color.black; ((TMP_Text)val2).font = GetDefaultFont(); ((TMP_Text)val2).alignment = (TextAlignmentOptions)513; ((TMP_Text)val2).fontStyle = (FontStyles)0; ((TMP_Text)val5).fontSize = 50f; ((Graphic)val5).color = Color.black; ((TMP_Text)val5).font = GetDefaultFont(); ((TMP_Text)val5).alignment = (TextAlignmentOptions)513; ((TMP_Text)val5).fontStyle = (FontStyles)0; } public static GameObject MakeMissingModsPanel(GameObject genericPanel) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) GameObject val = CopyGeneric(genericPanel, "MissingModsPanel"); Object.Destroy((Object)(object)GetProgressBar(val)); Object.Destroy((Object)(object)GetTextArea(val)); TextMeshProUGUI val2 = ((Component)val.transform.Find("NeededModList/Viewport/Content/tmprow/Background/Label")).gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val2).fontSize = 50f; ((Graphic)val2).color = Color32.op_Implicit(new Color32((byte)50, (byte)50, (byte)50, byte.MaxValue)); ((TMP_Text)val2).font = GetDefaultFont(); ((TMP_Text)val2).alignment = (TextAlignmentOptions)513; ((Graphic)val2).raycastTarget = false; ((TMP_Text)GetTitleText(val)).text = "Missing mods"; ((TMP_Text)GetInfoText(val)).text = "Install | Mod data"; return val; } public static GameObject MakeNoSyncerPanel(GameObject genericPanel) { GameObject val = CopyGeneric(genericPanel, "HostMissingSyncer"); Object.Destroy((Object)(object)GetProgressBar(val)); Object.Destroy((Object)(object)GetTextArea(val)); Object.Destroy((Object)(object)GetModList(val)); ((TMP_Text)GetTitleText(val)).text = "Warning!"; ((TMP_Text)GetInfoText(val)).text = "Host's missing BoplModSyncer"; return val; } public static GameObject MakeInstallingPanel(GameObject genericPanel) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown GameObject val = CopyGeneric(genericPanel, "InstallingPanel"); Object.Destroy((Object)(object)GetModList(val)); TextMeshProUGUI val2 = ((Component)val.transform.Find("ProgressBar/Percentage")).gameObject.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val2).fontSize = 50f; ((Graphic)val2).color = Color.black; ((TMP_Text)val2).font = GetDefaultFont(); ((TMP_Text)val2).alignment = (TextAlignmentOptions)513; ((TMP_Text)GetTitleText(val)).text = "Downloading!"; GameObject val3 = Object.Instantiate<GameObject>(((Component)GetOkButtonComp(val)).gameObject, val.transform); Button continueButton = val3.GetComponent<Button>(); ((UnityEvent)continueButton.onClick).AddListener((UnityAction)delegate { ((Component)continueButton).gameObject.SetActive(false); }); val3.SetActive(false); ((Object)val3).name = "Continue Button"; ((TMP_Text)((Component)val3.transform.Find("Text (TMP)")).GetComponent<TextMeshProUGUI>()).text = "Continue"; return val; } public static GameObject MakeRestartPanel(GameObject genericPanel) { GameObject val = CopyGeneric(genericPanel, "RestartPanel"); Object.Destroy((Object)(object)GetProgressBar(val)); Object.Destroy((Object)(object)GetTextArea(val)); Object.Destroy((Object)(object)GetModList(val)); ((TMP_Text)GetTitleText(val)).text = "Warning!"; ((TMP_Text)GetInfoText(val)).text = "Game will be restarted!"; return val; } public static GameObject InstantiatePanel(GameObject panelToInstantiate, UnityAction onOkClick = null) { //IL_0035: 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) if ((Object)(object)currentPanel != (Object)null) { throw new Exception("'" + ((Object)currentPanel).name + "' panel is already open!"); } Transform canvas = PanelUtils.GetCanvas(); Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name == "MainMenu") { MainMenu[] componentsInChildren = ((Component)canvas).GetComponentsInChildren<MainMenu>(); foreach (MainMenu val in componentsInChildren) { if (val.IsEnabled) { val.DisableAll(); currentMenu = val; break; } } } currentPanel = Object.Instantiate<GameObject>(panelToInstantiate, canvas); SetupButtons(currentPanel, onOkClick); return currentPanel; } } internal class PanelUtils { public static Transform GetCanvas() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (name == "MainMenu") { return GameObject.Find("Canvas (1)").transform; } if (name.Contains("Select")) { return GameObject.Find("Canvas").transform; } GameObject val = GameObject.Find("Canvas"); if ((Object)(object)val != (Object)null) { return val.transform; } val = new GameObject("Canvas"); val.AddComponent<Canvas>(); return val.transform; } } }