Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ItemFavoriteFramework v1.0.0
plugins/ItemFavoriteFramework.dll
Decompiled 20 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Versioning; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using ItemFavoriteFramework.Common; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using SimpleJson; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ItemFavoriteFramework")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ItemFavoriteFramework")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] namespace ItemFavoriteFramework { public static class API { public const int ApiVersion = 3; public const int MarkItemFavorite = 1; public const int MarkSlotFavorite = 2; public const int MarkTrash = 4; public const string MarkIdFavorite = "favorite"; public const string MarkIdTrash = "trash"; public const int RollPass = 1; public const int RollGreed = 2; public const int RollNeed = 3; public static int GetApiVersion() { return 3; } public static string GetPluginVersion() { return "1.0.0"; } public static string GetPluginId() { return "MidnightsFX.ItemFavoriteFramework"; } public static bool HasEndpoint(string name) { return GetEndpointNames().Contains(name); } public static List<string> GetEndpointNames() { return (from m in typeof(API).GetMethods(BindingFlags.Static | BindingFlags.Public) select m.Name).Distinct().OrderBy<string, string>((string n) => n, StringComparer.Ordinal).ToList(); } public static bool IsReady() { MarkData result; return MarkStore.TryGet(out result); } public static bool IsItemFavorited(ItemData item) { return Marks.IsItemFavorited(item); } public static bool IsItemTrashFlagged(ItemData item) { return Marks.IsItemTrashFlagged(item); } public static bool IsSlotFavorited(int x, int y) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return Marks.IsSlotFavorited(new Vector2i(x, y)); } public static bool IsItemInFavoritedSlot(ItemData item) { return Marks.IsItemInFavoritedSlot(item); } public static bool IsProtected(ItemData item) { return Marks.IsProtected(item); } public static int GetMarks(ItemData item) { return Marks.GetMarks(item); } public static List<ItemData> GetFavoritedItems(Inventory inventory) { return Marks.GetItems(inventory, Marks.IsItemFavorited); } public static List<ItemData> GetTrashFlaggedItems(Inventory inventory) { return Marks.GetItems(inventory, Marks.IsItemTrashFlagged); } public static List<ItemData> GetProtectedItems(Inventory inventory) { return Marks.GetItems(inventory, Marks.IsProtected); } public static List<Vector2i> GetFavoritedSlots() { if (!MarkStore.TryGet(out var result)) { return new List<Vector2i>(); } return (from s in result.Slots orderby s.y, s.x select s).ToList(); } public static bool SetItemFavorited(ItemData item, bool favorited) { return Succeeded(Marks.SetItemFavorited(item, favorited)); } public static bool SetItemTrashFlagged(ItemData item, bool trashFlagged) { return Succeeded(Marks.SetItemTrashFlagged(item, trashFlagged)); } public static bool SetSlotFavorited(int x, int y, bool favorited) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return Succeeded(Marks.SetSlotFavorited(new Vector2i(x, y), favorited)); } private static bool Succeeded(MarkResult result) { if (result != MarkResult.Changed) { return result == MarkResult.Unchanged; } return true; } public static void AddChangedListener(Action listener) { MarkStore.AddListener(listener); } public static void RemoveChangedListener(Action listener) { MarkStore.RemoveListener(listener); } public static Sprite GetFavoriteIcon() { return SpriteFactory.Star; } public static Sprite GetTrashIcon() { return SpriteFactory.Trash; } public static Color GetFavoriteColor() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return ValConfig.BorderColorFavoritedItem.Value; } public static Color GetFavoritedSlotColor() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return ValConfig.BorderColorFavoritedSlot.Value; } public static Color GetTrashColor() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return ValConfig.BorderColorTrashFlaggedItem.Value; } public static bool RegisterMarkType(string id, string group, Color color, Sprite icon, string tooltipToken) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return CustomMarks.Register(id, group, color, icon, tooltipToken); } public static bool IsMarkTypeRegistered(string id) { return CustomMarks.IsRegistered(id); } public static bool HasCustomMark(ItemData item, string id) { return CustomMarks.Has(item, id); } public static List<string> GetCustomMarks(ItemData item) { return CustomMarks.Get(item); } public static bool SetCustomMark(ItemData item, string id, bool marked) { return CustomMarks.Set(item, id, marked); } public static void AddMarkListener(Action<ItemData, string, bool> listener) { MarkEvents.Add(listener); } public static void RemoveMarkListener(Action<ItemData, string, bool> listener) { MarkEvents.Remove(listener); } public static void AddItemClickHandler(Func<Inventory, ItemData, int, bool> handler) { ItemClickHandlers.Add(handler); } public static void RemoveItemClickHandler(Func<Inventory, ItemData, int, bool> handler) { ItemClickHandlers.Remove(handler); } public static bool IsItemWishlisted(ItemData item) { return Wishlists.IsWishlisted(item); } public static string GetWishlistJson() { WishlistStore.TryGet(out var result); return Wishlists.ToJson(result); } public static bool AddWishlistEntry(string prefabName, string criteriaJson) { if (!Wishlists.TryParseCriteria(criteriaJson, out var criteria)) { return false; } WishResult wishResult = Wishlists.Add(prefabName, criteria, autoNeed: false); if (wishResult != WishResult.Added) { return wishResult == WishResult.Unchanged; } return true; } public static bool AddWishlistEntryFromItem(ItemData item) { WishResult wishResult = Wishlists.AddFromItem(item); if (wishResult != WishResult.Added) { return wishResult == WishResult.Unchanged; } return true; } public static int RemoveWishlistEntries(string prefabName) { return Wishlists.RemoveByPrefab(prefabName); } public static void AddWishlistChangedListener(Action listener) { WishlistStore.AddListener(listener); } public static void RemoveWishlistChangedListener(Action listener) { WishlistStore.RemoveListener(listener); } public static void OpenWishlistEditor() { WishlistPanel.Open(); } public static bool RegisterWishlistProvider(string providerId, Func<string, string> getSchemaJson, Func<ItemData, string, bool> matches, Func<string, string> describe, Func<ItemData, string> suggest) { return WishlistProviders.Register(providerId, getSchemaJson, matches, describe, suggest); } public static bool UnregisterWishlistProvider(string providerId) { return WishlistProviders.Unregister(providerId); } public static Sprite GetWishlistIcon() { return SpriteFactory.Heart; } public static Color GetWishlistColor() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return LootConfig.WishlistColor.Value; } public static void AddItemNameProvider(Func<ItemData, string> provider) { ItemNames.Add(provider); } public static void RemoveItemNameProvider(Func<ItemData, string> provider) { ItemNames.Remove(provider); } public static string GetServerFeaturesJson() { bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer(); bool value = (flag ? LootConfig.EnableLootRolls.Value : ClientSession.RollsEnabled); bool value2 = (flag ? LootConfig.EnableWishlistSharing.Value : ClientSession.WishlistsEnabled); return new JsonBuilder().BeginObject().Bool("iff", flag || ClientSession.Welcomed).Num("protocol", 1L) .Str("version", flag ? "1.0.0" : ClientSession.ServerVersion) .Bool("rolls", value) .Bool("wishlists", value2) .Num("rollSeconds", flag ? LootConfig.RollSeconds.Value : ClientSession.RollSeconds) .Num("reserveSeconds", flag ? (LootConfig.ReserveMinutes.Value * 60f) : ClientSession.ReserveSeconds) .Num("maxMembers", flag ? LootConfig.MaxRollMembers.Value : ClientSession.MaxRollMembers) .EndObject() .ToString(); } public static string GetConnectedPlayersJson() { JsonBuilder jsonBuilder = new JsonBuilder().BeginArray(); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { foreach (ServerPeer item in ServerSession.All) { if (item.Verified) { jsonBuilder.BeginObject().Id("player", item.Pid).Str("name", item.Name) .Bool("iff", item.HasIff) .Bool("sharesWishlist", item.SharesWishlist) .EndObject(); } } } else { foreach (ClientSession.DirEntry item2 in ClientSession.Directory) { jsonBuilder.BeginObject().Id("player", item2.Pid).Str("name", item2.Name) .Bool("iff", item2.HasIff) .Bool("sharesWishlist", item2.Shares) .EndObject(); } } return jsonBuilder.EndArray().ToString(); } public static string GetPlayerWishlistJson(long playerId) { if (playerId != 0L && playerId == WishlistStore.PlayerId) { return GetWishlistJson(); } WishlistRoster.Shared shared; return Wishlists.ToJson(WishlistRoster.TryGet(playerId, out shared) ? shared.Data : null); } public static string GetWantedByJson(ItemData item) { JsonBuilder jsonBuilder = new JsonBuilder().BeginArray(); if (Wishlists.IsWishlisted(item)) { jsonBuilder.BeginObject().Id("player", WishlistStore.PlayerId).Str("name", ((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerName() : "") .Bool("self", value: true) .EndObject(); } foreach (long item2 in Wishlists.WantedBy(item)) { jsonBuilder.BeginObject().Id("player", item2).Str("name", WishlistRoster.NameOf(item2)) .Bool("self", value: false) .EndObject(); } return jsonBuilder.EndArray().ToString(); } public static bool IsRollingAvailable() { if ((Object)(object)ZNet.instance == (Object)null) { return false; } if (!ZNet.instance.IsServer()) { return ClientSession.RollsEnabled; } return LootConfig.EnableLootRolls.Value; } public static bool StartRoll(ItemDrop drop, string playerIdsJson, string optionsJson, ref string rollId, ref string error) { string rollId2; string error2; bool result = RollClient.StartRoll(drop, Json.ParseIdList(playerIdsJson), optionsJson, out rollId2, out error2); rollId = rollId2; error = error2; return result; } public static bool StartRollForItem(ItemData item, Vector3 position, string playerIdsJson, string optionsJson, ref string rollId, ref string error) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (item == null || (Object)(object)item.m_dropPrefab == (Object)null) { rollId = ""; error = "invalidItem"; return false; } if (!IsRollingAvailable()) { rollId = ""; error = (((Object)(object)ZNet.instance == (Object)null) ? "notInWorld" : "disabled"); return false; } ItemDrop val = ItemDrop.DropItem(item, item.m_stack, position, Quaternion.identity); if (StartRoll(val, playerIdsJson, optionsJson, ref rollId, ref error)) { return true; } if ((Object)(object)val != (Object)null && (Object)(object)ZNetScene.instance != (Object)null) { ZNetScene.instance.Destroy(((Component)val).gameObject); } return false; } public static string GetRollStateJson(ItemDrop drop) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) JsonBuilder jsonBuilder = new JsonBuilder().BeginObject(); ZDO val = (((Object)(object)drop != (Object)null && (Object)(object)drop.m_nview != (Object)null && drop.m_nview.IsValid()) ? drop.m_nview.GetZDO() : null); long num = RollStamp.ReadNonce(val); if (num == 0L) { return jsonBuilder.Str("phase", "free").EndObject().ToString(); } LockState lockState = RollLocks.Resolve(val, RollLocks.LocalPid); jsonBuilder.Str("rollId", RollIds.Format(val.m_uid, num)).Str("phase", lockState switch { LockState.Free => "free", LockState.Rolling => "rolling", _ => "reserved", }); if (RollLocks.TryGetEntry(val.m_uid, out var entry) && entry.Nonce == num) { jsonBuilder.Id("winner", entry.WinnerPid).Str("winnerName", entry.WinnerName).Num("secondsLeft", RollLocks.SecondsLeft(val.m_uid)); } return jsonBuilder.Bool("lockedForMe", lockState == LockState.Rolling || lockState == LockState.ReservedForOther).EndObject().ToString(); } public static bool IsDropLocked(ItemDrop drop) { return RollLocks.Resolve(drop, 0L) != LockState.Free; } public static bool CanPlayerPickUp(ItemDrop drop, long playerId) { return !RollLocks.IsLockedFor(drop, playerId); } public static bool SubmitRollVote(string rollId, int choice) { if (choice < 1 || choice > 3) { return false; } return RollClient.Vote(rollId, (RollChoice)choice); } public static void AddRollListener(Action<string> listener) { RollEvents.Add(listener); } public static void RemoveRollListener(Action<string> listener) { RollEvents.Remove(listener); } public static string GetPlayerIdsNearJson(Vector3 point, float radius) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return RollHelpers.ToJson(RollHelpers.PlayersNear(point, radius)); } public static string GetDamagerPlayerIdsJson(Character character) { return RollHelpers.ToJson(RollHelpers.Damagers(character)); } } internal static class ConfigFileWatcher { private class WatchEntry { internal DateTime LastWriteUTC; internal long FileLength; internal Action<string> Callback; } internal class ConfigFileWatcherBehaviour : MonoBehaviour { private float nextPollTime; public void Update() { if (!(Time.unscaledTime < nextPollTime)) { nextPollTime = Time.unscaledTime + PollInterval(); Poll(); } } private static float PollInterval() { if (ValConfig.ConfigPollIntervalSeconds == null) { return 30f; } return ValConfig.ConfigPollIntervalSeconds.Value; } private static void Poll() { if (WatchedFiles.Count == 0) { return; } string[] array = WatchedFiles.Keys.ToArray(); foreach (string text in array) { if (!WatchedFiles.TryGetValue(text, out var value) || !File.Exists(text)) { continue; } FileInfo fileInfo = new FileInfo(text); DateTime lastWriteTimeUtc = fileInfo.LastWriteTimeUtc; long length = fileInfo.Length; if (!(lastWriteTimeUtc == value.LastWriteUTC) || length != value.FileLength) { value.LastWriteUTC = lastWriteTimeUtc; value.FileLength = length; try { value.Callback?.Invoke(text); } catch (Exception ex) { Logger.LogWarning("ConfigFileWatcher callback for " + text + " threw: " + ex.Message); } } } } } private const float FallbackPollSeconds = 30f; private static readonly Dictionary<string, WatchEntry> WatchedFiles = new Dictionary<string, WatchEntry>(StringComparer.OrdinalIgnoreCase); private static ConfigFileWatcherBehaviour watchProcess; internal static void Initialize() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)watchProcess != (Object)null)) { GameObject val = new GameObject("ItemFavoriteFramework_ConfigFileWatcher"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; watchProcess = val.AddComponent<ConfigFileWatcherBehaviour>(); Logger.LogDebug("ConfigFileWatcher initialized."); } } internal static void Register(string fullPath, Action<string> onChanged) { if (!string.IsNullOrEmpty(fullPath)) { DateTime lastWriteUTC = DateTime.MinValue; long fileLength = 0L; if (File.Exists(fullPath)) { FileInfo fileInfo = new FileInfo(fullPath); lastWriteUTC = fileInfo.LastWriteTimeUtc; fileLength = fileInfo.Length; } WatchedFiles[fullPath] = new WatchEntry { LastWriteUTC = lastWriteUTC, FileLength = fileLength, Callback = onChanged }; Logger.LogDebug("ConfigFileWatcher watching " + fullPath); } } internal static void RefreshStamp(string fullPath) { if (string.IsNullOrEmpty(fullPath) || !WatchedFiles.TryGetValue(fullPath, out var value)) { return; } try { FileInfo fileInfo = new FileInfo(fullPath); value.LastWriteUTC = fileInfo.LastWriteTimeUtc; value.FileLength = fileInfo.Length; } catch (Exception) { value.LastWriteUTC = DateTime.MinValue; value.FileLength = 0L; } } } internal static class ConfigNetwork { private static bool initialized; private static Harmony harmony; private static readonly HashSet<string> usedRpcNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly List<YamlConfigFile> editableFiles = new List<YamlConfigFile>(); private static bool connectionPatchApplied; private const byte EditProtocolVersion = 1; private const int MaxEditRequestBytes = 250000; internal static bool ServerConfigsSynced { get; private set; } internal static event Action<YamlConfigFile, bool, string> EditResult; internal static void Init() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown if (initialized) { return; } initialized = true; SynchronizationManager.OnConfigurationSynchronized += OnConfigurationSynchronized; try { harmony = new Harmony("MidnightsFX.ItemFavoriteFramework.config"); harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "Shutdown", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ConfigNetwork), "ResetOnWorldUnload", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { Logger.LogWarning("Could not patch ZNet.Shutdown for config sync teardown: " + ex.Message); } } internal static void RegisterFile(YamlConfigFile file) { //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_00ea: Expected O, but got Unknown if (file == null || file.Sync == ConfigSyncMode.LocalOnly) { return; } if (string.IsNullOrEmpty(file.RpcName)) { file.RpcName = "ItemFavoriteFramework_" + Path.GetFileNameWithoutExtension(file.FileName); } if (!usedRpcNames.Add(file.RpcName)) { Logger.LogError("Config RPC name '" + file.RpcName + "' is already in use; " + file.FileName + " will not be synced. Give it an explicit RpcName."); return; } file.Rpc = NetworkManager.Instance.AddRPC(file.RpcName, (CoroutineHandler)((long sender, ZPackage package) => OnServerReceive(file, sender, package)), (CoroutineHandler)((long sender, ZPackage package) => OnClientReceive(file, sender, package))); SynchronizationManager.Instance.AddInitialSynchronization(file.Rpc, (Func<ZPackage>)(() => SendFileAsZPackage(file))); if (!file.AllowAdminEdit) { return; } editableFiles.Add(file); PatchNewConnection(); if (!((Object)(object)ZNet.instance != (Object)null)) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { RegisterEditRpcs(ZNet.instance, peer, file); } } private static void PatchNewConnection() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown if (connectionPatchApplied) { return; } try { MethodInfo methodInfo = AccessTools.Method(typeof(ZNet), "OnNewConnection", (Type[])null, (Type[])null); if (methodInfo == null) { Logger.LogError("ZNet.OnNewConnection not found; admins cannot edit server configs remotely."); return; } harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(ConfigNetwork), "RegisterConnectionRpcs", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); connectionPatchApplied = true; } catch (Exception ex) { Logger.LogError("Could not patch ZNet.OnNewConnection for admin config edits: " + ex.Message); } } private static void RegisterConnectionRpcs(ZNet __instance, ZNetPeer peer) { foreach (YamlConfigFile editableFile in editableFiles) { RegisterEditRpcs(__instance, peer, editableFile); } } private static void RegisterEditRpcs(ZNet znet, ZNetPeer peer, YamlConfigFile file) { if (znet.IsServer()) { peer.m_rpc.Register<ZPackage>(EditRequestRpc(file), (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage package) { OnServerReceiveEdit(file, rpc, package); }); } else { peer.m_rpc.Register<ZPackage>(EditResultRpc(file), (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage package) { OnClientReceiveEditResult(file, package); }); } } private static string EditRequestRpc(YamlConfigFile file) { return "MidnightsFX.ItemFavoriteFramework." + file.RpcName + ".Edit"; } private static string EditResultRpc(YamlConfigFile file) { return "MidnightsFX.ItemFavoriteFramework." + file.RpcName + ".EditResult"; } internal static bool RequestEdit(YamlConfigFile file, string yaml, out string refusal) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown refusal = ""; if (file == null || !editableFiles.Contains(file) || !connectionPatchApplied) { refusal = "this config cannot be edited remotely."; return false; } if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer()) { refusal = "not connected to a server as a client."; return false; } if (SynchronizationManager.Instance != null && !SynchronizationManager.Instance.PlayerIsAdmin) { refusal = "only server admins can change this."; return false; } ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC == null) { refusal = "not connected to a server as a client."; return false; } ZPackage val = new ZPackage(); val.Write((byte)1); val.Write(yaml); if (val.Size() > 250000) { refusal = file.FileName + " is too large to send as an edit; change it on the server instead."; return false; } serverRPC.Invoke(EditRequestRpc(file), new object[1] { val }); return true; } private static void OnServerReceiveEdit(YamlConfigFile file, ZRpc rpc, ZPackage package) { ZNetPeer peer = ZNet.instance.GetPeer(rpc); if (peer == null || !peer.IsReady()) { return; } byte b = package.ReadByte(); if (b != 1) { SendEditResult(peer, file, accepted: false, $"This server expects edit protocol v{(byte)1}, " + $"the sender used v{b}. Update so both sides match."); return; } string yaml = package.ReadString(); string hostName = rpc.GetSocket().GetHostName(); string message; if (!ZNet.instance.IsAdmin(hostName)) { Logger.LogWarning("Rejecting an edit of " + file.FileName + " from non-admin " + peer.m_playerName + " (" + hostName + ")."); SendEditResult(peer, file, accepted: false, "Only server admins can change " + file.FileName + "."); } else if (!YamlConfigManager.ApplyEdited(file, yaml, out message)) { Logger.LogWarning("Admin " + peer.m_playerName + " (" + hostName + ") sent a " + file.FileName + " that was rejected: " + message); SendEditResult(peer, file, accepted: false, message); } else { Logger.LogInfo(file.FileName + " was replaced by admin " + peer.m_playerName + " (" + hostName + ")."); SendEditResult(peer, file, accepted: true, message); } } private static void OnClientReceiveEditResult(YamlConfigFile file, ZPackage package) { if (package.ReadByte() != 1) { ConfigNetwork.EditResult?.Invoke(file, arg2: false, "The server answered with an edit protocol this build does not understand."); return; } bool arg = package.ReadBool(); string arg2 = package.ReadString(); ConfigNetwork.EditResult?.Invoke(file, arg, arg2); } private static void SendEditResult(ZNetPeer peer, YamlConfigFile file, bool accepted, string message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write((byte)1); val.Write(accepted); val.Write(message ?? ""); peer.m_rpc.Invoke(EditResultRpc(file), new object[1] { val }); } internal static void Broadcast(YamlConfigFile file) { if (file != null && file.Rpc != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { file.Rpc.SendPackage(ZNet.instance.m_peers, SendFileAsZPackage(file)); } } internal static void ResetServerSyncState() { ServerConfigsSynced = false; } private static void OnConfigurationSynchronized(object sender, EventArgs e) { ServerConfigsSynced = true; } private static void ResetOnWorldUnload() { ResetServerSyncState(); } private static IEnumerator OnServerReceive(YamlConfigFile file, long sender, ZPackage package) { Logger.LogDebug($"Peer {sender} sent {file.FileName}; this config is server-authoritative, ignoring."); yield break; } private static IEnumerator OnClientReceive(YamlConfigFile file, long sender, ZPackage package) { string text = package.ReadString(); file.LoadFrom(text, ConfigOrigin.ServerSync); if (file.ClientWritesToDisk) { YamlConfigManager.WriteRawToDisk(file, text); } yield return null; } private static ZPackage SendFileAsZPackage(YamlConfigFile file) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); try { val.Write(File.Exists(file.Path) ? File.ReadAllText(file.Path) : file.SerializeCurrent()); } catch (Exception ex) { Logger.LogError("Could not read " + file.FileName + " to send to peers: " + ex.Message); val.Write(""); } return val; } } internal class ValidationReport { internal readonly List<string> Warnings = new List<string>(); internal readonly List<string> Errors = new List<string>(); internal bool HasErrors => Errors.Count > 0; internal ValidationReport Warn(string message) { Warnings.Add(message); return this; } internal ValidationReport Error(string message) { Errors.Add(message); return this; } internal ValidationReport Absorb(ValidationReport other) { if (other == null) { return this; } Warnings.AddRange(other.Warnings); Errors.AddRange(other.Errors); return this; } } internal static class ConfigValidation { internal static float Prefer(float bepInExValue, float yamlValue, float sentinel = 0f) { if (bepInExValue != sentinel) { return bepInExValue; } return yamlValue; } internal static string SuggestKey(string unknownKey, IEnumerable<string> knownKeys) { if (string.IsNullOrEmpty(unknownKey) || knownKeys == null) { return ""; } int num = Math.Max(1, unknownKey.Length / 4); string text = null; int num2 = int.MaxValue; foreach (string knownKey in knownKeys) { if (!string.IsNullOrEmpty(knownKey)) { int num3 = Distance(unknownKey, knownKey); if (num3 < num2) { num2 = num3; text = knownKey; } } } if (text == null || num2 > num) { return ""; } return " Did you mean '" + text + "'?"; } private static int Distance(string a, string b) { a = a.ToLowerInvariant(); b = b.ToLowerInvariant(); if (a == b) { return 0; } if (a.Length == 0) { return b.Length; } if (b.Length == 0) { return a.Length; } int[] array = new int[b.Length + 1]; int[] array2 = new int[b.Length + 1]; for (int i = 0; i <= b.Length; i++) { array[i] = i; } for (int j = 1; j <= a.Length; j++) { array2[0] = j; for (int k = 1; k <= b.Length; k++) { int num = ((a[j - 1] != b[k - 1]) ? 1 : 0); array2[k] = Math.Min(Math.Min(array2[k - 1] + 1, array[k] + 1), array[k - 1] + num); } int[] array3 = array; array = array2; array2 = array3; } return array[b.Length]; } } internal static class YamlConfigManager { internal static YamlConfigFile<ExampleSettings> ExampleFile; internal static YamlConfigFile<Dictionary<string, int>> ExampleSavedDataFile; private const string ExampleHeader = "#################################################\n# ItemFavoriteFramework - Example settings\n#\n# Entries is a map of <key>: <entry>. The key is the identity used elsewhere in this mod, so\n# renaming one is a breaking change; DisplayName is only a label and is safe to change.\n#\n# DisplayName string Shown to the player.\n# Multiplier float Scales the thing. 1.0 is unchanged. Range 0.1 - 10.\n# Mode enum Off | Add | Multiply\n# Prefabs list Prefab names this entry applies to. Unknown names are warned about\n# and skipped, they do not break the file.\n#\n# A typo in a key or an enum value costs you that one setting and logs a warning naming the\n# line; the rest of the file still loads.\n#################################################"; private static bool initialized; private static readonly List<YamlConfigFile> Files = new List<YamlConfigFile>(); private static readonly Dictionary<string, YamlConfigFile> ByPath = new Dictionary<string, YamlConfigFile>(StringComparer.OrdinalIgnoreCase); internal static IEnumerable<YamlConfigFile> All => Files; private static void RegisterConfigFiles() { RegisterExampleConfigs(); } private static void RegisterExampleConfigs() { ExampleFile = Register(new YamlConfigFile<ExampleSettings>("ExampleSettings.yaml") { Header = "#################################################\n# ItemFavoriteFramework - Example settings\n#\n# Entries is a map of <key>: <entry>. The key is the identity used elsewhere in this mod, so\n# renaming one is a breaking change; DisplayName is only a label and is safe to change.\n#\n# DisplayName string Shown to the player.\n# Multiplier float Scales the thing. 1.0 is unchanged. Range 0.1 - 10.\n# Mode enum Off | Add | Multiply\n# Prefabs list Prefab names this entry applies to. Unknown names are warned about\n# and skipped, they do not break the file.\n#\n# A typo in a key or an enum value costs you that one setting and logs a warning naming the\n# line; the rest of the file still loads.\n#################################################", Defaults = () => ExampleData.BuildDefaults(), Apply = delegate(ExampleSettings parsed) { ExampleData.Current = parsed; }, Validate = ExampleData.Validate, NeedsPrefabs = true, SchemaVersion = 1, GetSchemaVersion = (ExampleSettings settings) => settings.Version, SetSchemaVersion = delegate(ExampleSettings settings, int version) { settings.Version = version; } }); ExampleSavedDataFile = Register(new YamlConfigFile<Dictionary<string, int>>("ExampleSavedData.yaml") { SubFolder = "SavedData", Header = "# Save data written by this mod. Edit it with the game closed.", Defaults = () => new Dictionary<string, int>(), Apply = delegate(Dictionary<string, int> parsed) { ExampleData.SavedCounters = parsed; }, Sync = ConfigSyncMode.LocalOnly, Watch = false }); Register(new YamlConfigFile<Dictionary<string, ExampleEntry>>("ExampleLegacyFormat.yaml") { Header = "# A config kept in its original camelCase form for backwards compatibility.", Format = YamlFormat.CamelCase, Defaults = () => new Dictionary<string, ExampleEntry>(), Apply = delegate(Dictionary<string, ExampleEntry> parsed) { ExampleData.LegacyEntries = parsed; }, ClientWritesToDisk = true }); } internal static void Init() { if (!initialized) { initialized = true; ConfigNetwork.Init(); YamlFormat.AddTypeConverter((IYamlTypeConverter)(object)new TolerantEnumConverter()); RegisterConfigFiles(); ConfigFileWatcher.Initialize(); Logger.LogDebug($"Registered {Files.Count} yaml config files."); } } internal static TFile Register<TFile>(TFile file) where TFile : YamlConfigFile { if (file == null) { return null; } Files.Add(file); if (initialized) { Prepare(file); } return file; } internal static YamlConfigFile Find(string fileNameOrPath) { if (string.IsNullOrEmpty(fileNameOrPath)) { return null; } if (ByPath.TryGetValue(fileNameOrPath, out var value)) { return value; } for (int i = 0; i < Files.Count; i++) { if (string.Equals(Files[i].FileName, fileNameOrPath, StringComparison.OrdinalIgnoreCase)) { return Files[i]; } } return null; } internal static string ConfigDirectory(string subFolder = null) { string text = Path.Combine(Paths.ConfigPath, ValConfig.cfgFolder); if (!string.IsNullOrEmpty(subFolder)) { text = Path.Combine(text, subFolder); } return Directory.CreateDirectory(text).FullName; } internal static void ReloadFromDisk(YamlConfigFile file, bool broadcast = true) { if (file == null) { return; } try { if (!File.Exists(file.Path)) { Logger.LogWarning(file.FileName + " is no longer on disk; rewriting it with this mod's built-in defaults."); RestoreDefaults(file); } if (file.LoadFrom(File.ReadAllText(file.Path), ConfigOrigin.LocalFile) && broadcast) { ConfigNetwork.Broadcast(file); } } catch (Exception ex) { Logger.LogError("Could not reload " + file.FileName + ": " + ex.Message); } } internal static bool ApplyEdited(YamlConfigFile file, string yaml, out string message) { message = ""; if (file == null) { message = "no config file was named"; return false; } if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { message = file.FileName + " belongs to the server; changes have to be sent to it."; return false; } string parseError; ValidationReport validationReport = file.DryRun(yaml, out parseError); if (parseError != null) { message = file.FileName + " was rejected because " + parseError + "."; return false; } if (validationReport.HasErrors) { message = string.Join(" ", validationReport.Errors.ToArray()); return false; } if (!file.LoadFrom(yaml, ConfigOrigin.Api)) { message = file.LastError ?? (file.FileName + " could not be applied."); return false; } WriteRawToDisk(file, yaml); ConfigNetwork.Broadcast(file); message = ((validationReport.Warnings.Count == 0) ? "" : string.Join(" ", validationReport.Warnings.ToArray())); return true; } internal static string SerializeForEdit<T>(YamlConfigFile<T> file, T value) where T : class { if (file == null || value == null) { return ""; } return file.EffectiveFormat.Serializer.Serialize((object)value); } internal static void RestoreDefaults(YamlConfigFile file) { WriteRawToDisk(file, file?.SerializeDefaults()); } internal static void WriteCurrentToDisk(YamlConfigFile file) { WriteRawToDisk(file, file?.SerializeCurrent()); } internal static void WriteRawToDisk(YamlConfigFile file, string serializedYaml) { if (file == null || string.IsNullOrEmpty(file.Path)) { return; } try { Directory.CreateDirectory(Path.GetDirectoryName(file.Path)); using (StreamWriter streamWriter = new StreamWriter(file.Path)) { if (!string.IsNullOrEmpty(file.Header)) { streamWriter.WriteLine(file.Header); } streamWriter.WriteLine(serializedYaml); } ConfigFileWatcher.RefreshStamp(file.Path); } catch (Exception ex) { Logger.LogError("Could not write " + file.FileName + ": " + ex.Message); } } internal static void RevalidateAll() { for (int i = 0; i < Files.Count; i++) { try { Files[i].Revalidate(); } catch (Exception ex) { Logger.LogError("Revalidating " + Files[i].FileName + " threw: " + ex.Message); } } } internal static bool HasNoUsableConfig(string yamlText) { if (string.IsNullOrWhiteSpace(yamlText)) { return true; } string[] array = yamlText.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0 && !text.StartsWith("#") && !(text == "---") && !(text == "...")) { return false; } } return true; } private static void Prepare(YamlConfigFile file) { try { file.Path = Path.Combine(ConfigDirectory(file.SubFolder), file.FileName); ByPath[file.Path] = file; if (!File.Exists(file.Path)) { Logger.LogDebug(file.FileName + " missing, writing this mod's built-in defaults."); RestoreDefaults(file); } else if (HasNoUsableConfig(File.ReadAllText(file.Path))) { Logger.LogWarning(file.FileName + " was empty and has been overwritten with this mod's built-in defaults. File: " + file.Path); RestoreDefaults(file); } file.LoadFrom(File.Exists(file.Path) ? File.ReadAllText(file.Path) : "", ConfigOrigin.Startup); ConfigNetwork.RegisterFile(file); if (file.Watch) { ConfigFileWatcher.Register(file.Path, OnWatchedFileChanged); } } catch (Exception arg) { Logger.LogError($"Could not prepare {file.FileName}: {arg}"); } } private static void OnWatchedFileChanged(string path) { if (ByPath.TryGetValue(path, out var file)) { ConfigChangeDebouncer.Schedule(file, delegate { ReloadFromDisk(file); }); } } } public enum ExampleMode { Off, Add, Multiply } public class ExampleEntry { public string DisplayName { get; set; } [DefaultValue(1f)] public float Multiplier { get; set; } = 1f; public ExampleMode Mode { get; set; } public List<string> Prefabs { get; set; } = new List<string>(); } public class ExampleSettings { public int Version { get; set; } = 1; public Dictionary<string, ExampleEntry> Entries { get; set; } = new Dictionary<string, ExampleEntry>(); } internal static class ExampleData { internal static ExampleSettings Current = new ExampleSettings(); internal static Dictionary<string, int> SavedCounters = new Dictionary<string, int>(); internal static Dictionary<string, ExampleEntry> LegacyEntries = new Dictionary<string, ExampleEntry>(); internal static ExampleSettings BuildDefaults() { return new ExampleSettings { Version = 1, Entries = new Dictionary<string, ExampleEntry> { { "Example", new ExampleEntry { DisplayName = "An example", Multiplier = 1.5f, Mode = ExampleMode.Multiply } } } }; } internal static ValidationReport Validate(ExampleSettings next, ExampleSettings previous) { ValidationReport validationReport = new ValidationReport(); if (next.Entries == null || next.Entries.Count == 0) { return validationReport.Error("it defines no entries"); } foreach (KeyValuePair<string, ExampleEntry> entry in next.Entries) { if (entry.Value == null) { validationReport.Error("entry '" + entry.Key + "' has no settings under it"); continue; } if (entry.Value.Multiplier < 0.1f || entry.Value.Multiplier > 10f) { validationReport.Warn($"entry '{entry.Key}' has Multiplier {entry.Value.Multiplier}, outside the " + "supported range of 0.1 - 10. It will be used as written."); } if (entry.Value.Prefabs == null) { continue; } foreach (string prefab in entry.Value.Prefabs) { if (PrefabManager.Instance != null && (Object)(object)PrefabManager.Instance.GetPrefab(prefab) == (Object)null) { validationReport.Warn("entry '" + entry.Key + "' names prefab '" + prefab + "', which does not exist. That prefab will be skipped."); } } } if (previous != null && previous.Entries != null) { foreach (string key in previous.Entries.Keys) { if (!next.Entries.ContainsKey(key)) { validationReport.Warn("entry '" + key + "' was removed. Anything still referring to it will fall back."); } } } return validationReport; } } internal class TolerantEnumConverter : IYamlTypeConverter { private static readonly Dictionary<Type, object> fallbacks = new Dictionary<Type, object>(); internal static void SetFallback(Type enumType, object fallback) { if (!(enumType == null) && enumType.IsEnum) { fallbacks[enumType] = fallback; } } public bool Accepts(Type type) { if (type != null) { return type.IsEnum; } return false; } public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) Scalar val = ParserExtensions.Consume<Scalar>(parser); string value = val.Value; if (!string.IsNullOrWhiteSpace(value)) { try { return Enum.Parse(type, value.Trim(), ignoreCase: true); } catch (Exception) { } } object obj = FallbackFor(type); object[] array = new object[4]; Mark start = ((ParsingEvent)val).Start; array[0] = ((Mark)(ref start)).Line; array[1] = value; array[2] = type.Name; array[3] = obj; Logger.LogWarning(string.Format("line {0}: '{1}' is not a valid {2}. Using {3}. ", array) + "Valid values: " + string.Join(", ", Enum.GetNames(type)) + "."); return obj; } public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown emitter.Emit((ParsingEvent)new Scalar((value == null) ? "" : value.ToString())); } private static object FallbackFor(Type type) { if (fallbacks.TryGetValue(type, out var value)) { return value; } return Activator.CreateInstance(type); } } internal static class ConfigUI { internal class ConfigUIInputGuard : MonoBehaviour { private bool held; internal void Hold() { if (!held) { held = true; PushInputBlock(); } } public void OnDestroy() { if (held) { held = false; PopInputBlock(); } } } internal const float RowHeight = 34f; internal const float SubRowHeight = 26f; internal const float RowGap = 4f; internal const float CloseXSize = 28f; private static int inputBlockDepth; internal static void PushInputBlock() { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { inputBlockDepth++; if (inputBlockDepth == 1) { GUIManager.BlockInput(true); } } } internal static void PopInputBlock() { if (inputBlockDepth > 0) { inputBlockDepth--; if (inputBlockDepth == 0) { GUIManager.BlockInput(false); } } } internal static GameObject NewUI(string name, Transform parent, params Type[] components) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown GameObject val = new GameObject(name, components) { layer = 5 }; if ((Object)(object)val.GetComponent<RectTransform>() == (Object)null) { val.AddComponent<RectTransform>(); } val.transform.SetParent(parent, false); return val; } internal static GameObject NewRect(string name, Transform parent, float x, float y, float w, float h) { //IL_0020: 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_0030: 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) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) GameObject obj = NewUI(name, parent, typeof(RectTransform)); RectTransform val = (RectTransform)obj.transform; val.anchorMin = new Vector2(0f, 1f); val.anchorMax = new Vector2(0f, 1f); val.pivot = new Vector2(0f, 1f); val.sizeDelta = new Vector2(w, h); val.anchoredPosition = new Vector2(x, 0f - y); return obj; } internal static GameObject NewRow(Transform parent, float width, float height) { return NewRect("Row", parent, 0f, 0f, width, height); } internal static GameObject NewLayoutRow(Transform content, float width, float height) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) GameObject obj = NewUI("LayoutRow", content, typeof(RectTransform), typeof(LayoutElement)); RectTransform val = (RectTransform)obj.transform; val.anchorMin = new Vector2(0f, 1f); val.anchorMax = new Vector2(0f, 1f); val.pivot = new Vector2(0f, 1f); val.sizeDelta = new Vector2(width, height); LayoutElement component = obj.GetComponent<LayoutElement>(); component.minHeight = height; component.preferredHeight = height; component.minWidth = width; component.preferredWidth = width; return obj; } internal static void LayoutColumn(List<GameObject> rows, float x, float startY, float gap = 4f) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) float num = startY; foreach (GameObject row in rows) { if (!((Object)(object)row == (Object)null) && row.activeSelf) { RectTransform val = (RectTransform)row.transform; val.anchoredPosition = new Vector2(x, 0f - num); num += val.sizeDelta.y + gap; } } } internal static void PositionRow(GameObject row, float x, float y) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)row == (Object)null)) { ((RectTransform)row.transform).anchoredPosition = new Vector2(x, 0f - y); } } internal static GameObject CreatePanel(string title, float w, float h, out Transform body) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) GameObject val = GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), w, h, true); val.AddComponent<ConfigUIInputGuard>().Hold(); AddText(val.transform, 0f, 16f, w, 34f, title, 22, (TextAnchor)4, GUIManager.Instance.ValheimYellow); body = val.transform; return val; } internal static GameObject CreateScroll(Transform parent, float x, float y, float w, float h, out Transform content, out float contentWidth) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) GameObject val = NewRect("ScrollHolder", parent, x, y, w, h); GameObject val2 = GUIManager.Instance.CreateScrollView(val.transform, false, true, 8f, 4f, GUIManager.Instance.ValheimScrollbarHandleColorBlock, new Color(0f, 0f, 0f, 0.5f), w, h); content = val2.transform.Find("Scroll View/Viewport/Content"); contentWidth = w - 16f; ScrollRect componentInChildren = val2.GetComponentInChildren<ScrollRect>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.scrollSensitivity = 200f; } return val2; } internal static string L(string text) { if (string.IsNullOrEmpty(text)) { return ""; } if (Localization.instance == null) { return text; } return Localization.instance.Localize(text); } internal static string L(string token, string english) { string text = L(token); if (text.Length == 0 || text == token || text == "[" + token.TrimStart(new char[1] { '$' }) + "]") { return english; } return text; } internal static Text AddText(Transform parent, float x, float y, float w, float h, string text, int fontSize, TextAnchor anchor, Color? color = null) { //IL_0017: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GUIManager.Instance.CreateText(L(text), parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), GUIManager.Instance.AveriaSerifBold, fontSize, (Color)(((??)color) ?? GUIManager.Instance.ValheimBeige), true, Color.black, w, h, false); RectTransform val = (RectTransform)obj.transform; val.pivot = new Vector2(0f, 1f); val.anchoredPosition = new Vector2(x, 0f - y); Text component = obj.GetComponent<Text>(); component.alignment = anchor; component.horizontalOverflow = (HorizontalWrapMode)0; component.verticalOverflow = (VerticalWrapMode)0; return component; } internal static GameObject AddHeaderRow(Transform parent, float colWidth, string text, TextAnchor anchor = (TextAnchor)3) { //IL_0025: 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) GameObject obj = NewRow(parent, colWidth, 34f); AddText(obj.transform, 0f, 0f, colWidth, 34f, text, 18, anchor, GUIManager.Instance.ValheimYellow); return obj; } internal static GameObject AddTextRow(Transform parent, float colWidth, float height, string text, int fontSize, Color color, TextAnchor anchor = (TextAnchor)0) { //IL_001d: 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 = NewRow(parent, colWidth, height); AddText(obj.transform, 0f, 0f, colWidth, height, text, fontSize, anchor, color); return obj; } internal static GameObject AddSpacerRow(Transform parent, float colWidth, float height) { return NewRow(parent, colWidth, height); } internal static GameObject AddDividerRow(Transform parent, float colWidth, float height = 12f) { //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_0042: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) GameObject val = NewRow(parent, colWidth, height); GameObject obj = NewUI("Divider", val.transform, typeof(Image)); RectTransform val2 = (RectTransform)obj.transform; val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(0f, 1f); val2.pivot = new Vector2(0f, 1f); val2.sizeDelta = new Vector2(colWidth, 2f); val2.anchoredPosition = new Vector2(0f, 0f - height * 0.5f); Image component = obj.GetComponent<Image>(); ((Graphic)component).color = new Color(0.6f, 0.5f, 0.35f, 0.6f); ((Graphic)component).raycastTarget = false; return val; } internal static void SetMessages(Text target, IList<string> errors, IList<string> warnings) { if ((Object)(object)target == (Object)null) { return; } List<string> list = new List<string>(); if (errors != null) { foreach (string error in errors) { list.Add("<color=#F87171>" + error + "</color>"); } } if (warnings != null) { foreach (string warning in warnings) { list.Add("<color=#FBBF24>" + warning + "</color>"); } } target.text = string.Join("\n", list.ToArray()); } internal static GameObject AddButton(Transform parent, float x, float y, float w, string text, UnityAction onClick, float h = 40f) { //IL_0017: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) //IL_005f: Unknown result type (might be due to invalid IL or missing references) GameObject val = GUIManager.Instance.CreateButton(L(text), parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), w, h); RectTransform val2 = (RectTransform)val.transform; val2.pivot = new Vector2(0f, 1f); val2.anchoredPosition = new Vector2(x, 0f - y); if (onClick != null) { ((UnityEvent)val.GetComponent<Button>().onClick).AddListener(onClick); } return val; } internal static GameObject AddCloseX(Transform panel, float panelWidth, UnityAction onClick) { return AddButton(panel, panelWidth - 28f - 16f, 12f, 28f, "X", onClick, 28f); } internal static Toggle AddToggle(Transform parent, float x, float y, float size, bool value, Action<bool> onChange) { //IL_002e: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GUIManager.Instance.CreateToggle(parent, size, size); obj.transform.SetParent(parent, false); RectTransform val = (RectTransform)obj.transform; ((Transform)val).localScale = Vector3.one; val.anchorMin = new Vector2(0f, 1f); val.anchorMax = new Vector2(0f, 1f); val.pivot = new Vector2(0f, 1f); val.anchoredPosition = new Vector2(x, 0f - y); Toggle component = obj.GetComponent<Toggle>(); component.isOn = value; if (onChange != null) { ((UnityEvent<bool>)(object)component.onValueChanged).AddListener((UnityAction<bool>)delegate(bool b) { onChange(b); }); } return component; } internal static GameObject AddToggleRow(Transform parent, float colWidth, float labelW, string label, bool value, Action<bool> onChange, bool toggleOnLeft = false) { GameObject obj = NewRow(parent, colWidth, 34f); float x = (toggleOnLeft ? 0f : (labelW + 6f)); float x2 = (toggleOnLeft ? 34f : 0f); AddText(obj.transform, x2, 0f, labelW, 34f, label, 15, (TextAnchor)3); AddToggle(obj.transform, x, 3f, 26f, value, onChange); return obj; } internal static string Fmt(float v, bool whole) { if (!whole) { return v.ToString("0.00", CultureInfo.InvariantCulture); } return ((int)Mathf.Round(v)).ToString(CultureInfo.InvariantCulture); } internal static Slider BuildSlider(Transform parent, float x, float y, float width, float min, float max, float value, bool wholeNumbers) { //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_0042: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008a: 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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Expected O, but got Unknown //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Expected O, but got Unknown //IL_02a3: Unknown result type (might be due to invalid IL or missing references) GameObject val = NewUI("Slider", parent, typeof(RectTransform), typeof(Slider)); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(0f, 1f); val2.pivot = new Vector2(0f, 1f); val2.sizeDelta = new Vector2(width, 20f); val2.anchoredPosition = new Vector2(x, 0f - y); GameObject obj = NewUI("Background", val.transform, typeof(Image)); RectTransform val3 = (RectTransform)obj.transform; val3.anchorMin = new Vector2(0f, 0.25f); val3.anchorMax = new Vector2(1f, 0.75f); val3.sizeDelta = Vector2.zero; val3.anchoredPosition = Vector2.zero; ((Graphic)obj.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.6f); GameObject val4 = NewUI("Fill Area", val.transform, typeof(RectTransform)); RectTransform val5 = (RectTransform)val4.transform; val5.anchorMin = new Vector2(0f, 0.25f); val5.anchorMax = new Vector2(1f, 0.75f); val5.sizeDelta = new Vector2(-20f, 0f); val5.anchoredPosition = Vector2.zero; GameObject obj2 = NewUI("Fill", val4.transform, typeof(Image)); RectTransform val6 = (RectTransform)obj2.transform; val6.sizeDelta = new Vector2(10f, 0f); ((Graphic)obj2.GetComponent<Image>()).color = new Color(0.7f, 0.6f, 0.4f, 0.9f); GameObject val7 = NewUI("Handle Slide Area", val.transform, typeof(RectTransform)); RectTransform val8 = (RectTransform)val7.transform; val8.anchorMin = Vector2.zero; val8.anchorMax = Vector2.one; val8.sizeDelta = new Vector2(-20f, 0f); val8.anchoredPosition = Vector2.zero; GameObject obj3 = NewUI("Handle", val7.transform, typeof(Image)); RectTransform val9 = (RectTransform)obj3.transform; val9.sizeDelta = new Vector2(20f, 0f); Image component = obj3.GetComponent<Image>(); component.sprite = GUIManager.Instance.GetSprite("checkbox_marker"); component.type = (Type)1; Slider component2 = val.GetComponent<Slider>(); component2.fillRect = val6; component2.handleRect = val9; ((Selectable)component2).targetGraphic = (Graphic)(object)component; component2.direction = (Direction)0; component2.minValue = min; component2.maxValue = max; component2.wholeNumbers = wholeNumbers; component2.value = Mathf.Clamp(value, min, max); GUIManager.Instance.ApplySliderStyle(component2); return component2; } internal static GameObject AddSliderRow(Transform parent, float colWidth, float labelW, float sliderW, float valueW, string label, float min, float max, float value, bool wholeNumbers, Action<float> onChange) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) GameObject val = NewRow(parent, colWidth, 34f); AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3); Slider slider = BuildSlider(val.transform, labelW, 7f, sliderW, min, max, value, wholeNumbers); float num = labelW + sliderW + 10f; GameObject val2 = GUIManager.Instance.CreateInputField(val.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(num, -3f), (ContentType)(wholeNumbers ? 2 : 3), (string)null, 15, valueW, 28f); RectTransform val3 = (RectTransform)val2.transform; val3.pivot = new Vector2(0f, 1f); val3.anchoredPosition = new Vector2(num, -3f); InputField box = val2.GetComponent<InputField>(); box.SetTextWithoutNotify(Fmt(slider.value, wholeNumbers)); ((UnityEvent<float>)(object)slider.onValueChanged).AddListener((UnityAction<float>)delegate(float v) { if (wholeNumbers) { v = Mathf.Round(v); } box.SetTextWithoutNotify(Fmt(v, wholeNumbers)); onChange?.Invoke(v); }); ((UnityEvent<string>)(object)box.onEndEdit).AddListener((UnityAction<string>)delegate(string str) { if (!float.TryParse(str.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { result = slider.value; } result = Mathf.Clamp(result, min, max); if (wholeNumbers) { result = Mathf.Round(result); } box.SetTextWithoutNotify(Fmt(result, wholeNumbers)); if (slider.value != result) { slider.value = result; } else { onChange?.Invoke(result); } }); return val; } internal static InputField AddTextField(Transform parent, float x, float y, float w, string value, Action<string> onCommit, ContentType contentType = (ContentType)0, string placeholder = null, int charLimit = 0) { //IL_001e: 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_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) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GUIManager.Instance.CreateInputField(parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, 0f - y), contentType, placeholder, 15, w, 28f); RectTransform val = (RectTransform)obj.transform; val.pivot = new Vector2(0f, 1f); val.anchoredPosition = new Vector2(x, 0f - y); InputField component = obj.GetComponent<InputField>(); if (charLimit > 0) { component.characterLimit = charLimit; } component.SetTextWithoutNotify(value ?? ""); if (onCommit != null) { ((UnityEvent<string>)(object)component.onEndEdit).AddListener((UnityAction<string>)delegate(string s) { onCommit(s); }); } return component; } internal static GameObject AddTextFieldRow(Transform parent, float colWidth, float labelW, float fieldW, string label, string value, Action<string> onCommit, string placeholder = null, int charLimit = 0) { GameObject obj = NewRow(parent, colWidth, 34f); AddText(obj.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3); AddTextField(obj.transform, labelW + 6f, 3f, fieldW, value, onCommit, (ContentType)0, placeholder, charLimit); return obj; } internal static GameObject AddEnumCycleRow(Transform parent, float colWidth, float labelW, float ctrlW, string label, string[] options, int currentIndex, Action<int> onChange) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown GameObject val = NewRow(parent, colWidth, 34f); AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3); int idx = Mathf.Clamp(currentIndex, 0, Math.Max(0, options.Length - 1)); GameObject val2 = GUIManager.Instance.CreateButton((options.Length != 0) ? options[idx] : "", val.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(labelW + 6f, -2f), ctrlW, 28f); RectTransform val3 = (RectTransform)val2.transform; val3.pivot = new Vector2(0f, 1f); val3.anchoredPosition = new Vector2(labelW + 6f, -2f); Text caption = val2.GetComponentInChildren<Text>(); ((UnityEvent)val2.GetComponent<Button>().onClick).AddListener((UnityAction)delegate { if (options.Length != 0) { idx = (idx + 1) % options.Length; caption.text = options[idx]; onChange?.Invoke(idx); } }); return val; } internal static GameObject AddEnumFlagsRow(Transform parent, float colWidth, float labelW, string label, string[] names, Func<int, bool> isOn, Action<int, bool> set) { GameObject val = NewRow(parent, colWidth, 34f); AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3); float num = labelW + 6f; for (int i = 0; i < names.Length; i++) { int index = i; AddToggle(val.transform, num, 4f, 22f, isOn(index), delegate(bool on) { set(index, on); }); AddText(val.transform, num + 26f, 0f, 90f, 34f, names[index], 13, (TextAnchor)3); num += 120f; } return val; } internal static GameObject AddPickerRow(Transform parent, float colWidth, float labelW, float ctrlW, string label, string current, Func<IList<string>> options, Action<string> onPick, Func<string, bool> isKnown = null) { //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Expected O, but got Unknown GameObject val = NewRow(parent, colWidth, 34f); AddText(val.transform, 0f, 0f, labelW, 34f, label, 15, (TextAnchor)3); float num = ctrlW - 40f; InputField field = AddTextField(val.transform, labelW + 6f, 3f, num, current, delegate(string s) { onPick?.Invoke(s); }, (ContentType)0); Text marker = AddText(val.transform, labelW + 6f + num + 44f, 0f, 20f, 34f, "", 15, (TextAnchor)3, (Color?)new Color(0.98f, 0.75f, 0.14f)); Action refreshMarker = delegate { bool flag = isKnown != null && !string.IsNullOrEmpty(field.text) && !isKnown(field.text); marker.text = (flag ? "!" : ""); }; refreshMarker(); ((UnityEvent<string>)(object)field.onEndEdit).AddListener((UnityAction<string>)delegate { refreshMarker(); }); AddButton(val.transform, labelW + 6f + num + 4f, 3f, 34f, "...", (UnityAction)delegate { string title = label; IList<string> options2; if (options == null) { IList<string> list = new List<string>(); options2 = list; } else { options2 = options(); } ConfigUIPicker.ShowPicker(title, options2, field.text, delegate(string picked) { field.SetTextWithoutNotify(picked); refreshMarker(); onPick?.Invoke(picked); }); }, 28f); return val; } internal static GameObject AddStringListEditor(Transform content, float width, string label, List<string> items, Action onChanged, Func<IList<string>> options = null, Func<string, bool> isKnown = null) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Expected O, but got Unknown //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Expected O, but got Unknown GameObject val = NewLayoutRow(content, width, 26f); AddText(val.transform, 0f, 0f, width - 90f, 26f, label, 14, (TextAnchor)3, GUIManager.Instance.ValheimOrange); AddButton(val.transform, width - 84f, 0f, 80f, L("$piece_smelter_add", "Add"), (UnityAction)delegate { items.Add(""); onChanged?.Invoke(); }, 24f); for (int num = 0; num < items.Count; num++) { int index = num; GameObject val2 = NewLayoutRow(content, width, 26f); InputField field = AddTextField(val2.transform, 12f, 0f, width - 92f, items[index], delegate(string s) { items[index] = s; }, (ContentType)0); Text marker = AddText(val2.transform, width - 74f, 0f, 16f, 26f, "", 14, (TextAnchor)3, (Color?)new Color(0.98f, 0.75f, 0.14f)); Action refreshMarker = delegate { bool flag = isKnown != null && !string.IsNullOrEmpty(field.text) && !isKnown(field.text); marker.text = (flag ? "!" : ""); }; refreshMarker(); ((UnityEvent<string>)(object)field.onEndEdit).AddListener((UnityAction<string>)delegate { refreshMarker(); }); if (options != null) { AddButton(val2.transform, width - 56f, 0f, 26f, "...", (UnityAction)delegate { ConfigUIPicker.ShowPicker(label, options(), field.text, delegate(string picked) { field.SetTextWithoutNotify(picked); items[index] = picked; refreshMarker(); }); }, 24f); } AddButton(val2.transform, width - 26f, 0f, 24f, "x", (UnityAction)delegate { items.RemoveAt(index); onChanged?.Invoke(); }, 24f); } return val; } } internal enum FirstRunMode { Auto, ShowNextLaunch, Never } internal static class ConfigUIFirstRun { internal const string FolderName = "ModQuickConfig"; internal const string FileName = "FirstRun.cfg"; internal const string Section = "FirstRun"; internal const string QueueKeySuffix = ".FirstRun"; private const string LegacyPlaceholder = "\u0001"; private static string recordPath; private static readonly char[] InvalidKeyChars = new char[8] { '=', '\n', '\t', '\\', '"', '\'', '[', ']' }; internal static string RecordPath { get { if (recordPath == null) { recordPath = Path.Combine(Path.Combine(Application.persistentDataPath, "ModQuickConfig"), "FirstRun.cfg"); } return recordPath; } } internal static bool ShouldShow(string modKey, int revision, ConfigEntry<FirstRunMode> mode) { if (GUIManager.IsHeadless()) { return false; } return ModeOf(mode) switch { FirstRunMode.Never => false, FirstRunMode.ShowNextLaunch => true, _ => SeenRevision(modKey) < revision, }; } internal static int SeenRevision(string modKey) { if (GUIManager.IsHeadless() || !IsValidKey(modKey)) { return int.MaxValue; } string text = null; try { text = RecordPath; if (!File.Exists(text)) { return 0; } return Math.Max(0, BindSeen(OpenRecord(), modKey).Value); } catch (Exception ex) { Logger.LogWarning("Could not read the first-run record at " + text + ", so the '" + modKey + "' first-run popup is treated as seen: " + ex.Message); return int.MaxValue; } } internal static void MarkSeen(string modKey, int revision) { if (GUIManager.IsHeadless() || !IsValidKey(modKey) || revision <= 0) { return; } string text = null; try { text = RecordPath; ConfigFile val = OpenRecord(); ConfigEntry<int> val2 = BindSeen(val, modKey); if (val2.Value < revision) { val2.Value = revision; val.Save(); } } catch (Exception ex) { Logger.LogWarning("Could not write the first-run record at " + text + ": " + ex.Message); } } internal static bool QueueFirstRunPopup(string modKey, int revision, ConfigEntry<FirstRunMode> mode, int order, Func<bool> open, Func<bool> isOpen) { if (open == null || isOpen == null || !ShouldShow(modKey, revision, mode)) { return false; } return ConfigUIStartupPopups.Enqueue(modKey + ".FirstRun", order, () => OpenFirstRun(modKey, revision, mode, open), isOpen); } private static bool OpenFirstRun(string modKey, int revision, ConfigEntry<FirstRunMode> mode, Func<bool> open) { if (!ShouldShow(modKey, revision, mode)) { return false; } bool flag = false; if (mode != null && mode.Value == FirstRunMode.ShowNextLaunch) { mode.Value = FirstRunMode.Auto; flag = true; } bool flag2 = false; try { flag2 = open(); } finally { if (!flag2 && flag) { mode.Value = FirstRunMode.ShowNextLaunch; } } return flag2; } internal static bool TakeLegacyEntry(ConfigFile cfg, string section, string key, out string raw) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown raw = null; if (cfg == null || string.IsNullOrEmpty(section) || string.IsNullOrEmpty(key)) { return false; } bool saveOnConfigSet = cfg.SaveOnConfigSet; cfg.SaveOnConfigSet = false; try { ConfigDefinition val = new ConfigDefinition(section, key); if (cfg.ContainsKey(val)) { raw = cfg[val].GetSerializedValue(); } else { string value = cfg.Bind<string>(val, "\u0001", new ConfigDescription("Retired.", (AcceptableValueBase)null, Array.Empty<object>())).Value; raw = ((value == "\u0001") ? null : value); } cfg.Remove(val); if (saveOnConfigSet) { cfg.Save(); } } catch (Exception ex) { Logger.LogWarning("Could not read the retired setting [" + section + "] " + key + ": " + ex.Message); raw = null; } finally { cfg.SaveOnConfigSet = saveOnConfigSet; } return raw != null; } private static ConfigFile OpenRecord() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //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_0029: Expected O, but got Unknown BepInPlugin val = new BepInPlugin("ModQuickConfig.FirstRun", "ModQuickConfig first-run record - delete this file to see every mod's first-run popup again", "1.0.0"); return new ConfigFile(RecordPath, false, val) { SaveOnConfigSet = false }; } private static ConfigEntry<int> BindSeen(ConfigFile record, string modKey) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown return record.Bind<int>("FirstRun", modKey, 0, new ConfigDescription("The newest revision of this mod's first-run popup you have seen. 0 has not seen it; delete the line to see it again.", (AcceptableValueBase)null, Array.Empty<object>())); } private static FirstRunMode ModeOf(ConfigEntry<FirstRunMode> mode) { if (mode == null) { return FirstRunMode.Auto; } FirstRunMode value = mode.Value; if (!Enum.IsDefined(typeof(FirstRunMode), value)) { return FirstRunMode.Auto; } return value; } private static bool IsValidKey(string modKey) { if (string.IsNullOrEmpty(modKey) || modKey.Trim() != modKey || modKey.IndexOfAny(InvalidKeyChars) >= 0) { Logger.LogError("'" + modKey + "' cannot be a first-run record key; use a plain identifier."); return false; } return true; } } internal static class ConfigUILauncher { private static Component cachedBroker; private static MethodInfo cachedRegister; private static MethodInfo cachedUnregister; private static bool loggedOwner; internal static bool IsAvailable => (Object)(object)Resolve() != (Object)null; internal static void Init() { Resolve(); } internal static bool Register(string displayName, Action openPanel) { if (string.IsNullOrEmpty(displayName) || openPanel == null) { return false; } Component val = Resolve(); if ((Object)(object)val == (Object)null) { return false; } if (cachedRegister == null) { Logger.LogError("The QuickConfig launcher on this machine has no compatible Register method; '" + displayName + "' will not appear in it."); return false; } try { cachedRegister.Invoke(val, new object[2] { displayName, openPanel }); return true; } catch (Exception ex) { Logger.LogError("Could not register '" + displayName + "' with the QuickConfig launcher: " + ex.Message); return false; } } internal static void Unregister(string displayName) { if (string.IsNullOrEmpty(displayName)) { return; } Component val = Resolve(); if ((Object)(object)val == (Object)null || cachedUnregister == null) { return; } try { cachedUnregister.Invoke(val, new object[1] { displayName }); } catch (Exception ex) { Logger.LogWarning("Could not unregister '" + displayName + "' from the QuickConfig launcher: " + ex.Message); } } private static Component Resolve() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown if ((Object)(object)cachedBroker != (Object)null) { return cachedBroker; } cachedRegister = null; cachedUnregister = null; GameObject val = null; try { val = GameObject.Find("ModQuickConfigLauncher"); } catch (Exception) { } if ((Object)(object)val == (Object)null) { try { val = new GameObject("ModQuickConfigLauncher"); Object.DontDestroyOnLoad((Object)(object)val); val.AddComponent<QuickConfigBroker>(); } catch (Exception ex2) { Logger.LogError("Could not create the QuickConfig launcher: " + ex2.Message); return null; } } Component val2 = null; Component[] components = val.GetComponents<Component>(); foreach (Component val3 in components) { if ((Object)(object)val3 != (Object)null && ((object)val3).GetType().Name == "QuickConfigBroker") { val2 = val3; break; } } if ((Object)(object)val2 == (Object)null) { return null; } Type type = ((object)val2).GetType(); cachedRegister = type.GetMethod("Register", BindingFlags.Instance | BindingFlags.Public, null, new Type[2] { typeof(string), typeof(Action) }, null); cachedUnregister = type.GetMethod("Unregister", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (!loggedOwner) { loggedOwner = true; int num = 1; try { PropertyInfo property = type.GetProperty("BrokerVersion", BindingFlags.Instance | BindingFlags.Public); if (property != null) { num = (int)property.GetValue(val2, null); } } catch (Exception) { } Logger.LogInfo($"QuickConfig launcher v{num} owned by " + $"{type.Assembly.GetName().Name}; this mod carries v{2}."); if (num < 2) { Logger.LogInfo("That copy is older than this one. Registration still works; the launcher UI is whatever the owning mod shipped."); } } cachedBroker = val2; return cachedBroker; } } internal static class ConfigUIPicker { private const float PanelW = 420f; private const float PanelH = 520f; private const float RowH = 30f; private const int MaxRows = 400; private static GameObject overlay; internal static bool IsOpen => (Object)(object)overlay != (Object)null; internal static void ShowPicker(string title, IList<string> options, string current, Action<string> onPick) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Expected O, but got Unknown //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Expected O, but got Unknown Close(); if (GUIManager.Instance == null || (Object)(object)GUIManager.CustomGUIFront == (Object)null) { return; } List<string> all = new List<string>(); if (options != null) { foreach (string option in options) { if (!string.IsNullOrEmpty(option)) { all.Add(option); } } } all.Sort(StringComparer.OrdinalIgnoreCase); overlay = ConfigUI.NewUI("ConfigUIPickerOverlay", GUIManager.CustomGUIFront.transform, typeof(Image)); RectTransform val = (RectTransform)overlay.transform; val.anchorMin = Vector2.zero; val.anchorMax = Vector2.one; val.offsetMin = Vector2.zero; val.offsetMax = Vector2.zero; ((Graphic)overlay.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.35f); Button obj = overlay.AddComponent<Button>(); ((Selectable)obj).transition = (Transition)0; ((UnityEvent)obj.onClick).AddListener(new UnityAction(Close)); overlay.AddComponent<ConfigUI.ConfigUIInputGuard>().Hold(); GameObject obj2 = GUIManager.Instance.CreateWoodpanel(overlay.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), 420f, 520f, true); ConfigUI.AddText(obj2.transform, 0f, 14f, 420f, 30f, title, 18, (TextAnchor)4, GUIManager.Instance.ValheimYellow); InputField val2 = ConfigUI.AddTextField(obj2.transform, 16f, 52f, 320f, "", null, (ContentType)0, ConfigUI.L("$menu_filter", "Filter") + "..."); ConfigUI.AddButton(obj2.transform, 344f, 52f, 60f, ConfigUI.L("$menu_close", "Close"), new UnityAction(Close), 28f); ConfigUI.CreateScroll(obj2.transform, 16f, 92f, 388f, 404f, out var content, out var contentW); if ((Object)(object)content == (Object)null) { return; } Action<string> rebuild = null; rebuild = delegate(string needle) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown foreach (Transform item in content) { Object.Destroy((Object)(object)((Component)item).gameObject); } int num = 0; foreach (string item2 in all) { if (string.IsNullOrEmpty(needle) || item2.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0) { if (num >= 400) { break; } num++; string picked = item2; ConfigUI.AddButton(ConfigUI.NewLayoutRow(content, contentW, 30f).transform, 0f, 0f, contentW, picked, (UnityAction)delegate { Close(); onPick?.Invoke(picked); }, 28f); } } if (num == 0) { ConfigUI.AddText(ConfigUI.NewLayoutRow(content, contentW, 30f).transform, 4f, 0f, contentW, 30f, ConfigUI.L("$modquickconfig_no_matches", "No matches"), 14, (TextAnchor)3); } else if (num >= 400) { GameObject obj3 = ConfigUI.NewLayoutRow(content, contentW, 30f); ConfigUI.AddText(text: ConfigUI.L("$modquickconfig_more_matches", "... $1 more, type to narrow").Replace("$1", (all.Count - num).ToString()), parent: obj3.transform, x: 4f, y: 0f, w: contentW, h: 30f, fontSize: 13, anchor: (TextAnchor)3); } }; ((UnityEvent<string>)(object)val2.onValueChanged).AddListener((UnityAction<string>)delegate(string needle) { rebuild(needle); }); rebuild(""); } internal static void Close() { if (!((Object)(object)overlay == (Object)null)) { Object.Destroy((Object)(object)overlay); overlay = null; } } } internal static class ConfigUIPrompt { private const float PanelW = 460f; private static GameObject overlay; internal static void Show(string title, string label, string initial, string warning, Action<string> onAccept) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Expected O, but got Unknown //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Expected O, but got Unknown //IL_019c: Unknown result type (might be due to invalid IL or missing references) Close(); if (GUIManager.Instance != null && !((Object)(object)GUIManager.CustomGUIFront == (Object)null)) { bool flag = !string.IsNullOrEmpty(warning); float num = (flag ? 96f : 0f); float num2 = 190f + num; overlay = ConfigUI.NewUI("ConfigUIPromptOverlay", GUIManager.CustomGUIFront.transform, typeof(Image)); RectTransform val = (RectTransform)overlay.transform; val.anchorMin = Vector2.zero; val.anchorMax = Vector2.one; val.offsetMin = Vector2.zero; val.offsetMax = Vector2.zero; ((Graphic)overlay.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.45f); overlay.AddComponent<ConfigUI.ConfigUIInputGuard>().Hold(); GameObject val2 = GUIManager.Instance.CreateWoodpanel(overlay.transform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 0f), 460f, num2, true); ConfigUI.AddText(val2.transform, 0f, 14f, 460f, 30f, title, 18, (TextAnchor)4, GUIManager.Instance.ValheimYellow); float num3 = 56f; if (flag) { ConfigUI.AddText(val2.transform, 20f, num3, 420f, num - 8f, warning, 13, (TextAnchor)0, (Color?)new Color(0.98f, 0.75f, 0.14f)); num3 += num; } ConfigUI.AddText(val2.transform, 20f, num3, 120f, 28f, label, 15, (TextAnchor)3); InputField field = ConfigUI.AddTextField(val2.transform, 140f, num3, 300f, initial, null, (ContentType)0, null, 64); num3 += 46f; ConfigUI.AddButton(val2.transform, 20f, num3, 190f, ConfigUI.L("$menu_cancel", "Cancel"), new UnityAction(Close), 34f); ConfigUI.AddButton(val2.transform, 250f, num3, 190f, ConfigUI.L("$menu_ok", "OK"), (UnityAction)delegate { string text = field.text; Close(); onAccept?.Invoke(text); }, 34f); ((Selectable)field).Select(); field.ActivateInputField(); } } internal static void Close() { if (!((Object)(object)overlay == (Object)null)) { Object.Destroy((Object)(object)overlay); overlay = null; } } } internal static class ConfigUIStartupPopups { internal const int OrderWelcome = 100; internal const int OrderNotice = 200; private static Component cachedQueue; private static MethodInfo cachedEnqueue; private static MethodInfo cachedCancel; private static bool loggedOwner; internal static bool Enqueue(string key, int order, Func<bool> tryOpen, Func<bool> isOpen) { if (string.IsNullOrEmpty(key) || tryOpen == null || isOpen == null) { return false; } if (GUIManager.IsHeadless()) { return false; } Component val = Resolve(); if ((Object)(object)val == (Object)null) { return false; } if (cachedEnqueue == null) { Logger.LogError("The startup popup queue on this machine has no compatible Enqueue method; '" + key + "' will not be shown."); return false; } try { return (bool)cachedEnqueue.Invoke(val, new object[4] { key, order, tryOpen, isOpen }); } catch (Exception ex) { Logger.LogError("Could not queue the startup popup '" + key + "': " + ex.Message); return false; } } internal static void Cancel(string key) { if (string.IsNullOrEmpty(key) || GUIManager.IsHeadless()) { return; } Component val = Resolve(); if ((Object)(object)val == (Object)null || cachedCancel == null) { return; } try { cachedCancel.Invoke(val, new object[1] { key }); } catch (Exception ex) { Logger.LogWarning("Could not cancel the startup popup '" + key + "': " + ex.Message); } } private static Component Resolve() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown if ((Object)(object)cachedQueue != (Object)null) { return cachedQueue; } cachedEnqueue = null; cachedCancel = null; GameObject val = null; try { val = GameObject.Find("ModStartupPopupQueue"); } catch (Exception) { } if ((Object)(object)val == (Object)null) { try { val = new GameObject("ModStartupPopupQueue"); Object.DontDestroyOnLoad((Object)(object)val); val.AddComponent<StartupPopupQueue>(); } catch (Exception ex2) { Logger.LogError("Could not create the startup popup queue: " + ex2.Message); return null; } } Component val2 = null; Component[] components = val.GetComponents<Component>(); foreach (Component val3 in components) { if ((Object)(object)val3 != (Object)null && ((object)val3).GetType().Name == "StartupPopupQueue") { val2 = val3; break; } } if ((Object)(object)val2 == (Object)null) { try { val2 = (Component)(object)val.AddComponent<StartupPopupQueue>(); } catch (Exception ex3) { Logger.LogError("Could not create the startup popup queue: " + ex3.Message); return null; } } Type type = ((object)val2).GetType(); cachedEnqueue = type.GetMethod("Enqueue", BindingFlags.Instance | BindingFlags.Public, null, new Type[4] { typeof(string), typeof(int), typeof(Func<bool>), typeof(Func<bool>) }, null); cachedCancel = type.GetMethod("Cancel", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); if (!loggedOwner) { loggedOwner = true; int num = 0; try { PropertyInfo property = type.GetProperty("QueueVersion", BindingFlags.Instance | BindingFlags.Public); if (property != null) { num = (int)property.GetValue(val2, null); } } catch (Exception) { } Logger.LogInfo($"Startup popup queue v{num} owned by " + $"{type.Assembly.GetName().Name}; this mod carries v{1}."); } cachedQueue = val2; return cachedQueue; } } internal class QuickConfigBroker : MonoBehaviour { internal const string BrokerObjectName = "ModQuickConfigLauncher"; internal const string BrokerTypeName = "QuickConfigBroker"; internal const string ButtonObjectName = "ModQuickConfigButton"; internal const int ContractVersion = 2; private const float ButtonW = 150f; private const float ButtonH = 38f; private const float PanelW = 320f; private static QuickConfigBroker Instance; private static Harmony harmony; private readonly Dictionary<string, Action> entries = new Dictionary<string, Action>(StringComparer.Ordinal); private readonly List<string> order = new List<string>(); private GameObject mainMenuButton; private GameObject pauseButton; private GameObject listPanel; public int BrokerVersion => 2; public void Register(string modName, Action openPanel) { if (!string.IsNullOrEmpty(modName) && openPanel != null) { if (!entries.ContainsKey(modName)) { order.Add(modName); } entries[modName] = openPanel; RefreshVisibility(); } } public void Unregister(string modName) { if (!string.IsNullOrEmpty(modName)) { if (entries.Remove(modName)) { order.Remove(modName); } if ((Object)(object)listPanel != (Object)null) { CloseList(); } RefreshVisibility(); } } public bool IsRegistered(string modName) {