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 StorageGroups v0.1.7
plugins/StorageGroups.Core.dll
Decompiled 16 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using Microsoft.CodeAnalysis; using SmartContainerRouting.Core.Model; using SmartContainerRouting.Core.Text; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("jonasd")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+f556d01ba578811190b24467d0afe74ca948dcb8")] [assembly: AssemblyProduct("Storage Groups")] [assembly: AssemblyTitle("StorageGroups.Core")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SmartContainerRouting.Core.Text { public static class ContainerHoverText { public static string? BuildExtraLine(ContainerSettings settings, Func<string, string?> groupDisplayName, Func<string, bool>? groupEvictsMismatching = null, bool isWarded = false) { List<string> list = new List<string>(); string groupName = settings.GroupName; if (groupName != null) { list.Add("[" + (groupDisplayName(groupName) ?? groupName) + "]"); } string groupName2 = settings.GroupName; if (groupName2 != null && (groupEvictsMismatching?.Invoke(groupName2) ?? settings.IsEvicting)) { list.Add("Strict: only group items"); } if (isWarded) { list.Add("Routes within this ward only"); } if (list.Count != 0) { return string.Join(" | ", list); } return null; } } public static class DisplayNameSanitizer { public const int MaxLength = 32; public static string? Sanitize(string? value) { if (string.IsNullOrWhiteSpace(value)) { return null; } string text = StripMarkupCharacters(value.Trim()); if (text.Length == 0) { return null; } if (text.Length <= 32) { return text; } return text.Substring(0, 32); } private static string StripMarkupCharacters(string value) { StringBuilder stringBuilder = new StringBuilder(value.Length); foreach (char c in value) { if (c != '<' && c != '>') { stringBuilder.Append(c); } } return stringBuilder.ToString(); } } public static class GroupButtonHover { public const int MaxItems = 8; public static string Describe(string? groupName, IEnumerable<string> itemNames) { List<string> list = new List<string> { "Open group settings" }; if (groupName == null) { list.Add("No group assigned"); return string.Join("\n", list); } list.Add("Items in current group [" + groupName + "]:"); List<string> list2 = itemNames.Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string name) => name, StringComparer.Ordinal).ToList(); if (list2.Count == 0) { list.Add("(none yet, use Add Contents)"); } else { list.AddRange(list2.Take(8)); if (list2.Count > 8) { list.Add($"+{list2.Count - 8} more"); } } return string.Join("\n", list); } } public static class GroupNameNormalizer { public static string? Normalize(string? value) { return DisplayNameSanitizer.Sanitize(value)?.ToLowerInvariant(); } } public static class GroupNameSuggestion { public static string? Suggest(IEnumerable<KeyValuePair<string, int>> stacks, GroupRegistry registry) { string text = (from entry in (from @group in (from stack in stacks select new KeyValuePair<string, int>(DisplayNameSanitizer.Sanitize(stack.Key), stack.Value) into stack where stack.Key != null select stack).GroupBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> stack) => stack.Key, StringComparer.Ordinal) select (Name: @group.Key, Total: @group.Sum((KeyValuePair<string, int> stack) => stack.Value)) into entry orderby entry.Total descending select entry).ThenBy<(string, int), string>(((string Name, int Total) entry) => entry.Name, StringComparer.Ordinal) select entry.Name).FirstOrDefault(); if (text == null || !registry.HasGroup(text)) { return text; } int num = 2; string text3; while (true) { string text2 = " " + num.ToString(CultureInfo.InvariantCulture); text3 = text[..Math.Min(text.Length, 32 - text2.Length)].TrimEnd(Array.Empty<char>()) + text2; if (!registry.HasGroup(text3)) { break; } num++; } return text3; } } public static class RouteSummary { public sealed class Neighbour { public ContainerSettings Settings { get; } public double Distance { get; } public bool HasRoom { get; } public bool SameWard { get; } public Neighbour(ContainerSettings settings, double distance, bool hasRoom, bool sameWard) { Settings = settings; Distance = distance; HasRoom = hasRoom; SameWard = sameWard; } } public const int MaxGroupLines = 6; public static string Describe(ContainerSettings source, GroupRegistry groups, IEnumerable<Neighbour> neighbours, double range, bool held) { List<string> list = new List<string>(); if (held) { list.Add("Sorting paused"); } list.Add("Routes (" + Meters(range) + ")"); List<Neighbour> source2 = neighbours.Where((Neighbour n) => n.Distance <= range && n.Settings.GroupName != null && groups.GetGroup(n.Settings.GroupName) != null).ToList(); List<string> list2 = (from entry in (from @group in source2.GroupBy<Neighbour, string>((Neighbour n) => groups.GetGroup(n.Settings.GroupName).Name, StringComparer.OrdinalIgnoreCase) select DescribeGroup(@group.Key, @group.ToList()) into entry orderby entry.Distance select entry).ThenBy<(double, string), string>(((double Distance, string Text) entry) => entry.Text, StringComparer.Ordinal) select entry.Text).ToList(); if (list2.Count == 0) { list.Add("No group chests in range"); } else { list.AddRange(list2.Take(6)); if (list2.Count > 6) { list.Add($"+{list2.Count - 6} more"); } } IEnumerable<GroupDefinition?> first = source2.Select((Neighbour n) => groups.GetGroup(n.Settings.GroupName)); string groupName = source.GroupName; object second; if (groupName != null) { GroupDefinition groupDefinition = groups.GetGroup(groupName); if (groupDefinition != null) { second = new GroupDefinition[1] { groupDefinition }; goto IL_019d; } } second = new GroupDefinition[0]; goto IL_019d; IL_019d: List<string> list3 = (from @group in first.Concat<GroupDefinition>((IEnumerable<GroupDefinition>)second) where @group.EvictMismatching select @group.Name).Distinct<string>(StringComparer.OrdinalIgnoreCase).OrderBy<string, string>((string name) => name, StringComparer.Ordinal).ToList(); if (list3.Count > 0) { list.Add("Strict: " + string.Join(", ", list3.Select((string name) => "[" + name + "]"))); } return string.Join("\n", list); } private static (double Distance, string Text) DescribeGroup(string name, IReadOnlyList<Neighbour> chests) { List<Neighbour> list = chests.Where((Neighbour c) => c.SameWard).ToList(); if (list.Count == 0) { double num = chests.Min((Neighbour c) => c.Distance); return (Distance: num, Text: "[" + name + "] " + Meters(num) + ", ward edge"); } List<Neighbour> list2 = list.Where((Neighbour c) => c.HasRoom).ToList(); if (list2.Count <= 0) { return (Distance: list.Min((Neighbour c) => c.Distance), Text: "[" + name + "] " + Meters(list.Min((Neighbour c) => c.Distance)) + ", full"); } return (Distance: list2.Min((Neighbour c) => c.Distance), Text: "[" + name + "] " + Meters(list2.Min((Neighbour c) => c.Distance))); } private static string Meters(double meters) { return Math.Round(meters).ToString(CultureInfo.InvariantCulture) + " m"; } } public static class RoutingMessage { public static string Describe(string itemName, int amount, bool isEviction, string? destinationGroup, double distanceMeters, bool insideWard, bool relayed) { List<string> list = new List<string> { Math.Round(distanceMeters).ToString(CultureInfo.InvariantCulture) + " m" }; if (insideWard) { list.Add("inside ward"); } if (relayed) { list.Add("via another player"); } string text = ((destinationGroup == null) ? "an ungrouped chest" : ("[" + destinationGroup + "]")); return string.Format("{0} {1} {2} to {3} ({4})", isEviction ? "Evicted" : "Routed", amount, itemName, text, string.Join(", ", list)); } public static string Combine(IReadOnlyList<string> lines, int maxLines = 4) { List<string> list = new List<string>(lines.Take(maxLines)); if (lines.Count > maxLines) { list.Add($"and {lines.Count - maxLines} more"); } return string.Join("\n", list); } } } namespace SmartContainerRouting.Core.Routing { public static class ContainerRouter { public static ContainerRoutingCandidate? SelectRoute(ContainerSettings source, PlacedItem item, IEnumerable<ContainerRoutingCandidate> candidates, GroupRegistry groups, double range) { if (IsEligibleDestination(source, groups, item.ItemType)) { return null; } IReadOnlyCollection<ContainerRoutingCandidate> readOnlyCollection = (IReadOnlyCollection<ContainerRoutingCandidate>)(((object)(candidates as IReadOnlyCollection<ContainerRoutingCandidate>)) ?? ((object)candidates.ToArray())); ContainerRoutingCandidate containerRoutingCandidate = SelectDestination(item, readOnlyCollection, groups, range); if (containerRoutingCandidate != null) { return containerRoutingCandidate; } string groupName = source.GroupName; if (groupName != null) { GroupDefinition? groupDefinition = groups.GetGroup(groupName); if (groupDefinition != null && groupDefinition.EvictMismatching) { return SelectNearest(item, readOnlyCollection.Where((ContainerRoutingCandidate candidate) => candidate.Settings.GroupName == null), range) ?? SelectNearest(item, readOnlyCollection.Where(delegate(ContainerRoutingCandidate candidate) { string groupName2 = candidate.Settings.GroupName; if (groupName2 != null) { GroupDefinition? groupDefinition2 = groups.GetGroup(groupName2); if (groupDefinition2 == null) { return true; } return !groupDefinition2.EvictMismatching; } return false; }), range); } } return null; } public static ContainerRoutingCandidate? SelectDestination(PlacedItem item, IEnumerable<ContainerRoutingCandidate> candidates, GroupRegistry groups, double range) { string groupName = groups.GetGroupForItem(item.ItemType); if (groupName == null) { return null; } return SelectNearest(item, candidates.Where((ContainerRoutingCandidate c) => IsEligible(c.Settings, groupName)), range); } private static ContainerRoutingCandidate? SelectNearest(PlacedItem item, IEnumerable<ContainerRoutingCandidate> candidates, double range) { ContainerRoutingCandidate containerRoutingCandidate = null; double num = double.PositiveInfinity; foreach (ContainerRoutingCandidate candidate in candidates) { if (candidate.HasCapacityFor(item.ItemType)) { double num2 = item.Position.DistanceTo(candidate.Position); if (!(num2 > range) && (num2 < num || (num2 == num && (containerRoutingCandidate == null || string.CompareOrdinal(candidate.Id, containerRoutingCandidate.Id) < 0)))) { num = num2; containerRoutingCandidate = candidate; } } } return containerRoutingCandidate; } public static bool IsEligibleDestination(ContainerSettings settings, GroupRegistry groups, string itemType) { return IsEligible(settings, groups.GetGroupForItem(itemType)); } private static bool IsEligible(ContainerSettings settings, string? groupName) { if (groupName != null) { return string.Equals(settings.GroupName, groupName, StringComparison.OrdinalIgnoreCase); } return false; } } public sealed class ContainerRoutingCandidate { public string Id { get; } public Position Position { get; } public int FreeCapacity { get; } public IReadOnlyDictionary<string, int> PartialStackCapacity { get; } public ContainerSettings Settings { get; } public ContainerRoutingCandidate(string id, Position position, int freeCapacity, ContainerSettings settings, IReadOnlyDictionary<string, int>? partialStackCapacity = null) { Id = id; Position = position; FreeCapacity = freeCapacity; Settings = settings; PartialStackCapacity = partialStackCapacity ?? new Dictionary<string, int>(); } public bool HasCapacityFor(string itemType) { if (FreeCapacity <= 0) { if (PartialStackCapacity.TryGetValue(itemType, out var value)) { return value > 0; } return false; } return true; } public bool HasMoreCapacityThan(ContainerRoutingCandidate previous) { if (FreeCapacity > previous.FreeCapacity) { return true; } foreach (KeyValuePair<string, int> item in PartialStackCapacity) { if (item.Value > 0 && (!previous.PartialStackCapacity.TryGetValue(item.Key, out var value) || item.Value > value)) { return true; } } return false; } } public sealed class ItemTypeQuantity { public string ItemType { get; } public int Stack { get; } public ItemTypeQuantity(string itemType, int stack) { ItemType = itemType; Stack = stack; } } public static class NewArrivalFilter { public static Dictionary<string, int> ComputeBudget(IReadOnlyList<ItemTypeQuantity> knownStacks, IReadOnlyList<ItemTypeQuantity> currentStacks) { Dictionary<string, int> dictionary = SumByType(currentStacks); foreach (KeyValuePair<string, int> item in SumByType(knownStacks)) { int value; int num = (dictionary.TryGetValue(item.Key, out value) ? value : 0); dictionary[item.Key] = num - item.Value; } return dictionary; } public static IReadOnlyList<T> SelectWithinBudget<T>(Dictionary<string, int> budgetByType, IReadOnlyList<T> candidates, Func<T, ItemTypeQuantity> toStack) { List<T> list = new List<T>(); foreach (T candidate in candidates) { ItemTypeQuantity itemTypeQuantity = toStack(candidate); int value; int num = (budgetByType.TryGetValue(itemTypeQuantity.ItemType, out value) ? value : 0); if (num >= itemTypeQuantity.Stack) { list.Add(candidate); budgetByType[itemTypeQuantity.ItemType] = num - itemTypeQuantity.Stack; } } return list; } public static IReadOnlyList<T> SelectGenuinelyNew<T>(IReadOnlyList<ItemTypeQuantity> knownStacks, IReadOnlyList<ItemTypeQuantity> currentStacks, IReadOnlyList<T> candidates, Func<T, ItemTypeQuantity> toStack) { return SelectWithinBudget(ComputeBudget(knownStacks, currentStacks), candidates, toStack); } private static Dictionary<string, int> SumByType(IReadOnlyList<ItemTypeQuantity> stacks) { Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (ItemTypeQuantity stack in stacks) { dictionary[stack.ItemType] = (dictionary.TryGetValue(stack.ItemType, out var value) ? value : 0) + stack.Stack; } return dictionary; } } public sealed class PlacedItem { public string ItemType { get; } public Position Position { get; } public PlacedItem(string itemType, Position position) { ItemType = itemType; Position = position; } } public readonly struct Position : IEquatable<Position> { public float X { get; } public float Y { get; } public float Z { get; } public Position(float x, float y, float z) { X = x; Y = y; Z = z; } public double DistanceTo(Position other) { float num = X - other.X; float num2 = Y - other.Y; float num3 = Z - other.Z; return Math.Sqrt(num * num + num2 * num2 + num3 * num3); } public bool Equals(Position other) { if (X.Equals(other.X) && Y.Equals(other.Y)) { return Z.Equals(other.Z); } return false; } public override bool Equals(object? obj) { if (obj is Position other) { return Equals(other); } return false; } public override int GetHashCode() { return ((17 * 31 + X.GetHashCode()) * 31 + Y.GetHashCode()) * 31 + Z.GetHashCode(); } } public sealed class RelayLedger<TPayload> { public sealed class PendingRequest { public long RequestId { get; } public string DestinationId { get; } public TPayload Payload { get; } public double SentAt { get; internal set; } public int Attempts { get; internal set; } internal PendingRequest(long requestId, string destinationId, TPayload payload, double sentAt) { RequestId = requestId; DestinationId = destinationId; Payload = payload; SentAt = sentAt; Attempts = 1; } } public sealed class DueRequests { public IReadOnlyList<PendingRequest> Resend { get; } public IReadOnlyList<PendingRequest> GiveUp { get; } internal DueRequests(IReadOnlyList<PendingRequest> resend, IReadOnlyList<PendingRequest> giveUp) { Resend = resend; GiveUp = giveUp; } } private readonly double _resendAfter; private readonly int _maxAttempts; private readonly double _cooldown; private readonly Dictionary<long, PendingRequest> _pending = new Dictionary<long, PendingRequest>(); private readonly Dictionary<string, double> _cooldownUntil = new Dictionary<string, double>(StringComparer.Ordinal); private readonly Random _random = new Random(); public int PendingCount => _pending.Count; public IEnumerable<PendingRequest> Pending => _pending.Values; public RelayLedger(double resendAfterSeconds, int maxAttempts, double cooldownSeconds) { _resendAfter = resendAfterSeconds; _maxAttempts = maxAttempts; _cooldown = cooldownSeconds; } public long Start(string destinationId, TPayload payload, double now) { long num; do { byte[] array = new byte[8]; _random.NextBytes(array); num = BitConverter.ToInt64(array, 0); } while (num == 0L || _pending.ContainsKey(num)); _pending[num] = new PendingRequest(num, destinationId, payload, now); return num; } public bool TryComplete(long requestId, out PendingRequest request) { if (_pending.TryGetValue(requestId, out request)) { _pending.Remove(requestId); return true; } return false; } public DueRequests Tick(double now) { List<PendingRequest> list = new List<PendingRequest>(); List<PendingRequest> list2 = new List<PendingRequest>(); foreach (PendingRequest value in _pending.Values) { if (!(now - value.SentAt < _resendAfter)) { if (value.Attempts >= _maxAttempts) { list2.Add(value); continue; } value.Attempts++; value.SentAt = now; list.Add(value); } } foreach (PendingRequest item in list2) { _pending.Remove(item.RequestId); MarkRejected(item.DestinationId, now); } return new DueRequests(list, list2); } public void MarkRejected(string destinationId, double now) { _cooldownUntil[destinationId] = now + _cooldown; } public bool IsCoolingDown(string destinationId, double now) { if (_cooldownUntil.TryGetValue(destinationId, out var value)) { return now < value; } return false; } } public static class RelayReceipts { public const int DefaultCapacity = 64; public static bool TryFind(string? stored, long sender, long requestId, out int added) { foreach (var item in Parse(stored)) { if (item.Sender == sender && item.RequestId == requestId) { added = item.Added; return true; } } added = 0; return false; } public static string Add(string? stored, long sender, long requestId, int added, int capacity = 64) { List<(long, long, int)> list = Parse(stored).ToList(); list.Add((sender, requestId, added)); return string.Join(";", from entry in list.Skip(Math.Max(0, list.Count - capacity)) select string.Join(":", entry.Sender.ToString(CultureInfo.InvariantCulture), entry.RequestId.ToString(CultureInfo.InvariantCulture), entry.Added.ToString(CultureInfo.InvariantCulture))); } private static IEnumerable<(long Sender, long RequestId, int Added)> Parse(string? stored) { if (string.IsNullOrEmpty(stored)) { yield break; } string[] array = stored.Split(new char[1] { ';' }); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length == 3 && long.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && long.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2) && int.TryParse(array2[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result3)) { yield return (Sender: result, RequestId: result2, Added: result3); } } } } public static class WardWall { public static bool CanRoute(IEnumerable<string> sourceWardIds, IEnumerable<string> destinationWardIds) { return new HashSet<string>(sourceWardIds, StringComparer.Ordinal).SetEquals(destinationWardIds); } } } namespace SmartContainerRouting.Core.Persistence { public static class ContainerSettingsCodec { public static ContainerSettings Decode(Func<string, string> getString, Func<string, bool> getBool, GroupRegistry? groups = null) { string name = getString("SCR_Name"); string groupName = GroupReference.Resolve(getString("SCR_GroupId"), getString("SCR_Group"), groups); bool evictNonGroupItems = getBool("SCR_EvictNonGroup"); return new ContainerSettings(name, groupName, evictNonGroupItems); } public static void Encode(ContainerSettings settings, Action<string, string> setString, Action<string, bool> setBool, GroupRegistry? groups = null, string? knownGroupId = null, Func<string, string>? getExisting = null) { string text = GroupReference.IdFor(settings.GroupName, groups); if (text.Length == 0 && settings.GroupName != null && !string.IsNullOrEmpty(knownGroupId)) { text = knownGroupId; } if (text.Length == 0 && groups == null && settings.GroupName != null && getExisting != null && GroupNameNormalizer.Normalize(getExisting("SCR_Group")) == settings.GroupName) { text = getExisting("SCR_GroupId") ?? string.Empty; } setString("SCR_Name", settings.Name ?? string.Empty); setString("SCR_Group", settings.GroupName ?? string.Empty); setString("SCR_GroupId", text); setBool("SCR_EvictNonGroup", settings.EvictNonGroupItems); } } public sealed class GroupRegistryCache { private readonly Func<string?, GroupRegistry> _decode; private bool _hasValue; private string? _source; private GroupRegistry _registry = GroupRegistry.Empty; public GroupRegistryCache(Func<string?, GroupRegistry> decode) { _decode = decode ?? throw new ArgumentNullException("decode"); } public GroupRegistry Get(string? source) { if (!_hasValue || !string.Equals(_source, source, StringComparison.Ordinal)) { _source = source; _registry = _decode(source); _hasValue = true; } return _registry; } public void Set(string? source, GroupRegistry registry) { _source = source; _registry = registry; _hasValue = true; } public bool TryGet(string? source, out GroupRegistry registry) { if (source == null) { registry = GroupRegistry.Empty; return false; } registry = Get(source); return true; } } public static class GroupRegistryCodec { private sealed class GroupDto { public string? Id { get; set; } public string? Name { get; set; } = string.Empty; public long CreatorId { get; set; } public bool EvictMismatching { get; set; } public List<string?>? ItemTypes { get; set; } = new List<string>(); } public static string Serialize(GroupRegistry registry) { StringBuilder stringBuilder = new StringBuilder("["); bool flag = true; foreach (GroupDefinition item in registry.Groups.OrderBy<GroupDefinition, string>((GroupDefinition group) => group.Name, StringComparer.Ordinal)) { stringBuilder.Append(flag ? "{" : ",{"); Json.WriteMember(stringBuilder, "id", item.Id, first: true); Json.WriteMember(stringBuilder, "name", item.Name); Json.WriteMember(stringBuilder, "creatorId", item.CreatorId); Json.WriteMember(stringBuilder, "evictMismatching", item.EvictMismatching); Json.WriteMember(stringBuilder, "itemTypes", item.ItemTypes.OrderBy<string, string>((string item) => item, StringComparer.Ordinal)); stringBuilder.Append('}'); flag = false; } return stringBuilder.Append(']').ToString(); } public static GroupRegistry Deserialize(string? json) { TryDeserialize(json, out GroupRegistry registry); return registry; } public static bool TryDeserialize(string? json, out GroupRegistry registry) { if (string.IsNullOrWhiteSpace(json)) { registry = GroupRegistry.Empty; return true; } List<GroupDto> list; try { list = ReadGroups(Json.Parse(json)); } catch (FormatException) { registry = GroupRegistry.Empty; return false; } GroupRegistry groupRegistry = GroupRegistry.Empty; foreach (GroupDto item in list) { if (item == null) { continue; } string text = DisplayNameSanitizer.Sanitize(item.Name); if (text == null) { continue; } groupRegistry = groupRegistry.WithGroup(text, item.CreatorId, item.Id); if (!groupRegistry.HasGroup(text)) { continue; } if (item.EvictMismatching) { groupRegistry = groupRegistry.WithEvictMismatching(text, value: true); } foreach (string item2 in item.ItemTypes ?? new List<string>()) { if (!string.IsNullOrWhiteSpace(item2)) { groupRegistry = groupRegistry.WithItemAssignedToGroup(item2, text); } } } registry = groupRegistry; return true; } private static List<GroupDto?> ReadGroups(Json.JsonValue root) { if (root.Kind == Json.JsonKind.Null) { return new List<GroupDto>(); } if (root.Kind != Json.JsonKind.Array) { throw new FormatException("Expected an array of groups."); } List<GroupDto> list = new List<GroupDto>(root.Items.Count); foreach (Json.JsonValue item in root.Items) { if (item.Kind == Json.JsonKind.Null) { list.Add(null); continue; } if (item.Kind != Json.JsonKind.Object) { throw new FormatException("Expected a group object."); } GroupDto groupDto = new GroupDto(); foreach (KeyValuePair<string, Json.JsonValue> member in item.Members) { switch (member.Key) { case "id": groupDto.Id = member.Value.AsNullableString(); break; case "name": groupDto.Name = member.Value.AsNullableString(); break; case "creatorId": groupDto.CreatorId = member.Value.AsLong(); break; case "evictMismatching": groupDto.EvictMismatching = member.Value.AsBool(); break; case "itemTypes": groupDto.ItemTypes = member.Value.AsNullableStringList(); break; } } list.Add(groupDto); } return list; } } public static class GroupRegistryMutationCodec { private sealed class MutationDto { public string? Kind { get; set; } public string? GroupName { get; set; } public string? NewGroupName { get; set; } public string? ItemType { get; set; } public List<string>? ItemTypes { get; set; } public long PlayerId { get; set; } public bool EvictMismatching { get; set; } public string? GroupId { get; set; } } public static string Serialize(GroupRegistryMutation mutation) { StringBuilder stringBuilder = new StringBuilder("{"); Json.WriteMember(stringBuilder, "kind", mutation.Kind.ToString(), first: true); Json.WriteMember(stringBuilder, "groupName", mutation.GroupName); Json.WriteMember(stringBuilder, "newGroupName", mutation.NewGroupName); Json.WriteMember(stringBuilder, "itemType", mutation.ItemType); Json.WriteMember(stringBuilder, "itemTypes", mutation.ItemTypes); Json.WriteMember(stringBuilder, "playerId", mutation.PlayerId); Json.WriteMember(stringBuilder, "evictMismatching", mutation.EvictMismatching); Json.WriteMember(stringBuilder, "groupId", mutation.GroupId); return stringBuilder.Append('}').ToString(); } public static GroupRegistryMutation? Deserialize(string? json) { if (string.IsNullOrWhiteSpace(json)) { return null; } MutationDto mutationDto; try { mutationDto = ReadMutation(Json.Parse(json)); } catch (FormatException) { return null; } if (mutationDto?.Kind == null || !Enum.TryParse<GroupRegistryMutationKind>(mutationDto.Kind, out var result)) { return null; } switch (result) { case GroupRegistryMutationKind.CreateGroup: { string groupName5 = mutationDto.GroupName; if (groupName5 != null) { return GroupRegistryMutation.CreateGroup(groupName5, mutationDto.PlayerId, mutationDto.GroupId); } break; } case GroupRegistryMutationKind.DeleteGroup: { string groupName4 = mutationDto.GroupName; if (groupName4 != null) { return GroupRegistryMutation.DeleteGroup(groupName4, mutationDto.PlayerId); } break; } case GroupRegistryMutationKind.RenameGroup: { string groupName6 = mutationDto.GroupName; if (groupName6 != null) { string newGroupName = mutationDto.NewGroupName; if (newGroupName != null) { return GroupRegistryMutation.RenameGroup(groupName6, newGroupName, mutationDto.PlayerId); } } break; } case GroupRegistryMutationKind.AssignItem: { string itemType = mutationDto.ItemType; if (itemType != null) { string groupName2 = mutationDto.GroupName; if (groupName2 != null) { return GroupRegistryMutation.AssignItem(itemType, groupName2); } } break; } case GroupRegistryMutationKind.UnassignItem: { string itemType2 = mutationDto.ItemType; if (itemType2 != null) { return GroupRegistryMutation.UnassignItem(itemType2); } break; } case GroupRegistryMutationKind.ReplaceGroupItems: { string groupName3 = mutationDto.GroupName; if (groupName3 != null) { List<string> itemTypes = mutationDto.ItemTypes; if (itemTypes != null) { return GroupRegistryMutation.ReplaceGroupItems(groupName3, itemTypes, mutationDto.PlayerId); } } break; } case GroupRegistryMutationKind.SetGroupEviction: { string groupName = mutationDto.GroupName; if (groupName != null) { return GroupRegistryMutation.SetGroupEviction(groupName, mutationDto.EvictMismatching, mutationDto.PlayerId); } break; } } return null; } private static MutationDto? ReadMutation(Json.JsonValue root) { if (root.Kind == Json.JsonKind.Null) { return null; } if (root.Kind != Json.JsonKind.Object) { throw new FormatException("Expected a mutation object."); } MutationDto mutationDto = new MutationDto(); foreach (KeyValuePair<string, Json.JsonValue> member in root.Members) { switch (member.Key) { case "kind": mutationDto.Kind = member.Value.AsNullableString(); break; case "groupName": mutationDto.GroupName = member.Value.AsNullableString(); break; case "newGroupName": mutationDto.NewGroupName = member.Value.AsNullableString(); break; case "itemType": mutationDto.ItemType = member.Value.AsNullableString(); break; case "itemTypes": mutationDto.ItemTypes = member.Value.AsNullableStringList(); break; case "playerId": mutationDto.PlayerId = member.Value.AsLong(); break; case "evictMismatching": mutationDto.EvictMismatching = member.Value.AsBool(); break; case "groupId": mutationDto.GroupId = member.Value.AsNullableString(); break; } } return mutationDto; } } internal static class Json { public enum JsonKind { Null, True, False, Number, String, Array, Object } internal sealed class JsonValue { private readonly string? _text; private readonly bool _hasLoneSurrogate; public static readonly JsonValue Null = new JsonValue(JsonKind.Null); public static readonly JsonValue True = new JsonValue(JsonKind.True); public static readonly JsonValue False = new JsonValue(JsonKind.False); public JsonKind Kind { get; } public List<JsonValue> Items { get; } = new List<JsonValue>(); public List<KeyValuePair<string, JsonValue>> Members { get; } = new List<KeyValuePair<string, JsonValue>>(); private JsonValue(JsonKind kind, string? text = null, bool hasLoneSurrogate = false) { Kind = kind; _text = text; _hasLoneSurrogate = hasLoneSurrogate; } public static JsonValue Number(string text) { return new JsonValue(JsonKind.Number, text); } public static JsonValue String(string text, bool hasLoneSurrogate) { return new JsonValue(JsonKind.String, text, hasLoneSurrogate); } public static JsonValue Array() { return new JsonValue(JsonKind.Array); } public static JsonValue Object() { return new JsonValue(JsonKind.Object); } public string? AsNullableString() { if (Kind == JsonKind.Null) { return null; } if (Kind != JsonKind.String || _hasLoneSurrogate) { throw new FormatException("Expected a string."); } return _text; } public long AsLong() { if (Kind != JsonKind.Number || _text.IndexOfAny(new char[3] { '.', 'e', 'E' }) >= 0 || !long.TryParse(_text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result)) { throw new FormatException("Expected an integer."); } return result; } public bool AsBool() { return Kind switch { JsonKind.True => true, JsonKind.False => false, _ => throw new FormatException("Expected true or false."), }; } public List<string?>? AsNullableStringList() { if (Kind == JsonKind.Null) { return null; } if (Kind != JsonKind.Array) { throw new FormatException("Expected an array."); } List<string> list = new List<string>(Items.Count); foreach (JsonValue item in Items) { list.Add(item.AsNullableString()); } return list; } } private sealed class Parser { private readonly string _text; private int _position; private int _depth; public Parser(string text) { _text = text; } public JsonValue ParseDocument() { SkipWhitespace(); JsonValue result = ParseValue(); SkipWhitespace(); if (_position != _text.Length) { throw Error("Unexpected content after the JSON value."); } return result; } private JsonValue ParseValue() { if (_position >= _text.Length) { throw Error("Unexpected end of input."); } switch (_text[_position]) { case '{': return ParseObject(); case '[': return ParseArray(); case '"': return ParseString(); case 't': Expect("true"); return JsonValue.True; case 'f': Expect("false"); return JsonValue.False; case 'n': Expect("null"); return JsonValue.Null; default: return ParseNumber(); } } private JsonValue ParseObject() { Enter(); _position++; JsonValue jsonValue = JsonValue.Object(); SkipWhitespace(); if (TryConsume('}')) { _depth--; return jsonValue; } do { SkipWhitespace(); if (_position >= _text.Length || _text[_position] != '"') { throw Error("Expected a property name."); } string key = ParseString().AsNullableString(); SkipWhitespace(); if (!TryConsume(':')) { throw Error("Expected ':'."); } SkipWhitespace(); jsonValue.Members.Add(new KeyValuePair<string, JsonValue>(key, ParseValue())); SkipWhitespace(); if (TryConsume('}')) { _depth--; return jsonValue; } } while (TryConsume(',')); throw Error("Expected ',' or '}'."); } private JsonValue ParseArray() { Enter(); _position++; JsonValue jsonValue = JsonValue.Array(); SkipWhitespace(); if (TryConsume(']')) { _depth--; return jsonValue; } do { SkipWhitespace(); jsonValue.Items.Add(ParseValue()); SkipWhitespace(); if (TryConsume(']')) { _depth--; return jsonValue; } } while (TryConsume(',')); throw Error("Expected ',' or ']'."); } private JsonValue ParseString() { _position++; StringBuilder stringBuilder = new StringBuilder(); while (true) { if (_position >= _text.Length) { throw Error("Unterminated string."); } char c = _text[_position++]; if (c == '"') { break; } if (c < ' ') { throw Error("Control character in string."); } if (c != '\\') { stringBuilder.Append(c); continue; } if (_position >= _text.Length) { throw Error("Unterminated escape."); } switch (_text[_position++]) { case '"': stringBuilder.Append('"'); break; case '\\': stringBuilder.Append('\\'); break; case '/': stringBuilder.Append('/'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; case 'u': { if (_position + 4 > _text.Length || !ushort.TryParse(_text.Substring(_position, 4), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var result)) { throw Error("Invalid \\u escape."); } stringBuilder.Append((char)result); _position += 4; break; } default: throw Error("Invalid escape."); } } string text = stringBuilder.ToString(); return JsonValue.String(text, HasLoneSurrogate(text)); } private JsonValue ParseNumber() { int position = _position; TryConsume('-'); if (!TryConsume('0') && !ConsumeDigits()) { throw Error("Invalid value."); } if (TryConsume('.') && !ConsumeDigits()) { throw Error("Invalid number."); } if (_position < _text.Length && (_text[_position] == 'e' || _text[_position] == 'E')) { _position++; if (!TryConsume('+')) { TryConsume('-'); } if (!ConsumeDigits()) { throw Error("Invalid number."); } } return JsonValue.Number(_text.Substring(position, _position - position)); } private bool ConsumeDigits() { int position = _position; while (_position < _text.Length && _text[_position] >= '0' && _text[_position] <= '9') { _position++; } return _position > position; } private void Expect(string literal) { if (string.CompareOrdinal(_text, _position, literal, 0, literal.Length) != 0) { throw Error("Invalid literal."); } _position += literal.Length; } private void Enter() { if (++_depth > 64) { throw Error("Nested too deeply."); } } private bool TryConsume(char expected) { if (_position < _text.Length && _text[_position] == expected) { _position++; return true; } return false; } private void SkipWhitespace() { while (true) { bool flag = _position < _text.Length; if (flag) { bool flag2; switch (_text[_position]) { case '\t': case '\n': case '\r': case ' ': flag2 = true; break; default: flag2 = false; break; } flag = flag2; } if (flag) { _position++; continue; } break; } } private FormatException Error(string message) { return new FormatException($"{message} (at {_position})"); } private static bool HasLoneSurrogate(string value) { for (int i = 0; i < value.Length; i++) { if (char.IsHighSurrogate(value[i]) && i + 1 < value.Length && char.IsLowSurrogate(value[i + 1])) { i++; } else if (char.IsSurrogate(value[i])) { return true; } } return false; } } private const int MaxDepth = 64; public static void WriteString(StringBuilder output, string? value) { if (value == null) { output.Append("null"); return; } output.Append('"'); for (int i = 0; i < value.Length; i++) { char c = value[i]; switch (c) { case '\\': output.Append("\\\\"); continue; case '\b': output.Append("\\b"); continue; case '\f': output.Append("\\f"); continue; case '\n': output.Append("\\n"); continue; case '\r': output.Append("\\r"); continue; case '\t': output.Append("\\t"); continue; } if (char.IsHighSurrogate(c) && i + 1 < value.Length && char.IsLowSurrogate(value[i + 1])) { AppendEscaped(output, c); AppendEscaped(output, value[++i]); } else if (char.IsSurrogate(c)) { AppendEscaped(output, '\ufffd'); } else if (c < ' ' || c > '~' || c == '"' || c == '&' || c == '\'' || c == '+' || c == '<' || c == '>' || c == '`') { AppendEscaped(output, c); } else { output.Append(c); } } output.Append('"'); } public static void WriteMember(StringBuilder output, string name, string? value, bool first = false) { AppendName(output, name, first); WriteString(output, value); } public static void WriteMember(StringBuilder output, string name, long value) { AppendName(output, name, first: false); output.Append(value.ToString(CultureInfo.InvariantCulture)); } public static void WriteMember(StringBuilder output, string name, bool value) { AppendName(output, name, first: false); output.Append(value ? "true" : "false"); } public static void WriteMember(StringBuilder output, string name, IEnumerable<string?>? values) { AppendName(output, name, first: false); if (values == null) { output.Append("null"); return; } output.Append('['); bool flag = true; foreach (string value in values) { if (!flag) { output.Append(','); } WriteString(output, value); flag = false; } output.Append(']'); } private static void AppendName(StringBuilder output, string name, bool first) { if (!first) { output.Append(','); } output.Append('"').Append(name).Append("\":"); } private static void AppendEscaped(StringBuilder output, char c) { StringBuilder stringBuilder = output.Append("\\u"); int num = c; stringBuilder.Append(num.ToString("X4", CultureInfo.InvariantCulture)); } public static JsonValue Parse(string text) { return new Parser(text).ParseDocument(); } } public static class PersistenceKeys { public const string ContainerName = "SCR_Name"; public const string ContainerGroup = "SCR_Group"; public const string ContainerGroupId = "SCR_GroupId"; public const string ContainerEvictNonGroupItems = "SCR_EvictNonGroup"; public const string ContainerRelayReceipts = "SCR_RelayReceipts"; public const string GroupRegistryBlob = "SCR_Groups"; public const string GroupRegistryCorruptBackup = "SCR_GroupsCorruptBackup"; } } namespace SmartContainerRouting.Core.Model { public sealed class ContainerSettings { public string? Name { get; } public string? GroupName { get; } public bool EvictNonGroupItems { get; } public bool IsEvicting { get { if (EvictNonGroupItems) { return GroupName != null; } return false; } } public static ContainerSettings Default { get; } = new ContainerSettings(); public ContainerSettings(string? name = null, string? groupName = null, bool evictNonGroupItems = false) { Name = NormalizeOrNull(name); GroupName = GroupNameNormalizer.Normalize(groupName); EvictNonGroupItems = evictNonGroupItems; } public ContainerSettings WithName(string? name) { return new ContainerSettings(name, GroupName, EvictNonGroupItems); } public ContainerSettings WithGroupName(string? groupName) { return new ContainerSettings(Name, groupName, EvictNonGroupItems); } public ContainerSettings WithEvictNonGroupItems(bool value) { return new ContainerSettings(Name, GroupName, value); } public bool Equals(ContainerSettings? other) { if (other != null && string.Equals(Name, other.Name, StringComparison.Ordinal) && string.Equals(GroupName, other.GroupName, StringComparison.Ordinal)) { return EvictNonGroupItems == other.EvictNonGroupItems; } return false; } public override bool Equals(object? obj) { return Equals(obj as ContainerSettings); } public override int GetHashCode() { return ((17 * 31 + (Name?.GetHashCode() ?? 0)) * 31 + (GroupName?.GetHashCode() ?? 0)) * 31 + EvictNonGroupItems.GetHashCode(); } private static string? NormalizeOrNull(string? value) { return DisplayNameSanitizer.Sanitize(value); } } public sealed class GroupDefinition { public string Name { get; } public string Id { get; } public ItemTypeSet ItemTypes { get; } public bool EvictMismatching { get; } public long CreatorId { get; } private GroupDefinition(string id, string name, ItemTypeSet itemTypes, long creatorId, bool evictMismatching = false) { Id = id; Name = name; ItemTypes = itemTypes; CreatorId = creatorId; EvictMismatching = evictMismatching; } public static string LegacyIdFor(string name) { return "name:" + GroupNameNormalizer.Normalize(name); } public static GroupDefinition Empty(string name, long creatorId = 0L, bool evictMismatching = false, string? id = null) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Group name must not be empty.", "name"); } return new GroupDefinition(string.IsNullOrWhiteSpace(id) ? LegacyIdFor(name) : id, name, ItemTypeSet.Empty, creatorId, evictMismatching); } internal GroupDefinition WithItem(string itemType) { return new GroupDefinition(Id, Name, ItemTypes.Add(itemType), CreatorId, EvictMismatching); } internal GroupDefinition WithoutItem(string itemType) { return new GroupDefinition(Id, Name, ItemTypes.Remove(itemType), CreatorId, EvictMismatching); } internal GroupDefinition WithItems(ItemTypeSet itemTypes) { return new GroupDefinition(Id, Name, itemTypes, CreatorId, EvictMismatching); } internal GroupDefinition WithEvictMismatching(bool value) { return new GroupDefinition(Id, Name, ItemTypes, CreatorId, value); } } public static class GroupItemAdditions { public static IReadOnlyList<string> Missing(GroupRegistry registry, string groupName, IEnumerable<string> chestItemTypes) { GroupDefinition group = registry.GetGroup(groupName); if (group == null) { return Array.Empty<string>(); } return chestItemTypes.Where((string itemType) => !string.IsNullOrEmpty(itemType) && !group.ItemTypes.Contains(itemType)).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string itemType) => itemType, StringComparer.Ordinal) .ToList(); } } public static class GroupItemTransfers { public static string? Describe(GroupRegistry registry, string targetGroupName, IEnumerable<string> itemTypes, Func<string, string> itemDisplayName) { string targetName = registry.GetGroup(targetGroupName)?.Name ?? targetGroupName; string[] array = (from @group in (from itemType in itemTypes.Distinct<string>(StringComparer.Ordinal) select (itemType: itemType, owner: registry.GetGroupForItem(itemType)) into pair where pair.owner != null && !string.Equals(pair.owner, targetName, StringComparison.OrdinalIgnoreCase) select pair).GroupBy<(string, string), string>(((string itemType, string owner) pair) => pair.owner, StringComparer.Ordinal).OrderBy<IGrouping<string, (string, string)>, string>((IGrouping<string, (string itemType, string owner)> @group) => @group.Key, StringComparer.Ordinal) select string.Join(", ", @group.Select(((string itemType, string owner) pair) => itemDisplayName(pair.itemType)).OrderBy<string, string>((string name) => name, StringComparer.Ordinal)) + " moved from " + @group.Key + " to " + targetName).ToArray(); if (array.Length != 0) { return string.Join("\n", array); } return null; } } public static class GroupReference { public static string? Resolve(string? storedId, string? storedName, GroupRegistry? registry) { string text = GroupNameNormalizer.Normalize(storedName); if (registry == null) { return text; } GroupDefinition groupDefinition = ((!string.IsNullOrEmpty(storedId)) ? registry.GetGroupById(storedId) : ((text == null) ? null : (registry.GetGroupById(GroupDefinition.LegacyIdFor(text)) ?? registry.GetGroup(text)))); if (groupDefinition != null) { return GroupNameNormalizer.Normalize(groupDefinition.Name); } return text; } public static string IdFor(string? groupName, GroupRegistry? registry) { object obj; if (groupName != null) { obj = registry?.GetGroup(groupName)?.Id; if (obj == null) { return string.Empty; } } else { obj = string.Empty; } return (string)obj; } } public sealed class GroupRegistry { private readonly Dictionary<string, GroupDefinition> _groups; public static GroupRegistry Empty { get; } = new GroupRegistry(new Dictionary<string, GroupDefinition>()); public IReadOnlyCollection<GroupDefinition> Groups => (IReadOnlyCollection<GroupDefinition>)(object)_groups.Values.ToArray(); private GroupRegistry(Dictionary<string, GroupDefinition> groups) { _groups = groups; } public bool HasGroup(string groupName) { string text = GroupNameNormalizer.Normalize(groupName); if (text != null) { return _groups.ContainsKey(text); } return false; } public GroupDefinition? GetGroup(string groupName) { string text = GroupNameNormalizer.Normalize(groupName); if (text == null || !_groups.TryGetValue(text, out GroupDefinition value)) { return null; } return value; } public GroupDefinition? GetGroupById(string? groupId) { if (!string.IsNullOrEmpty(groupId)) { return _groups.Values.FirstOrDefault((GroupDefinition group) => string.Equals(group.Id, groupId, StringComparison.Ordinal)); } return null; } public GroupRegistry WithGroup(string groupName, long creatorId = 0L, string? groupId = null) { string? obj = DisplayNameSanitizer.Sanitize(groupName) ?? throw new ArgumentException("Group name must not be empty.", "groupName"); string key = GroupNameNormalizer.Normalize(obj); GroupDefinition groupDefinition = GroupDefinition.Empty(obj, creatorId, evictMismatching: false, groupId); if (!_groups.ContainsKey(key) && GetGroupById(groupDefinition.Id) == null) { return new GroupRegistry(With(_groups, key, groupDefinition)); } return this; } public GroupRegistry WithoutGroup(string groupName) { string text = GroupNameNormalizer.Normalize(groupName); if (text == null) { return this; } if (!_groups.ContainsKey(text)) { return this; } return new GroupRegistry(Without(_groups, text)); } public GroupRegistry WithRenamedGroup(string oldName, string newName) { string text = DisplayNameSanitizer.Sanitize(newName); if (text == null) { throw new ArgumentException("Group name must not be empty.", "newName"); } string text2 = GroupNameNormalizer.Normalize(oldName); if (!_groups.TryGetValue(text2, out GroupDefinition value)) { throw new ArgumentException("Group '" + oldName + "' does not exist.", "oldName"); } if (string.Equals(value.Name, text, StringComparison.Ordinal)) { return this; } string text3 = GroupNameNormalizer.Normalize(text); if (text2 != text3 && _groups.ContainsKey(text3)) { throw new ArgumentException("Group '" + text + "' already exists.", "newName"); } GroupDefinition groupDefinition = GroupDefinition.Empty(text, value.CreatorId, value.EvictMismatching, value.Id).WithItems(value.ItemTypes); if (!(text2 == text3)) { return new GroupRegistry(With(Without(_groups, text2), text3, groupDefinition)); } return new GroupRegistry(With(_groups, text2, groupDefinition)); } public GroupRegistry WithItemAssignedToGroup(string itemType, string groupName) { if (string.IsNullOrWhiteSpace(itemType)) { throw new ArgumentException("Item type must not be empty.", "itemType"); } string text = GroupNameNormalizer.Normalize(groupName); if (text == null || !_groups.TryGetValue(text, out GroupDefinition _)) { throw new ArgumentException("Group '" + groupName + "' does not exist.", "groupName"); } Dictionary<string, GroupDefinition> dictionary = new Dictionary<string, GroupDefinition>(_groups); foreach (KeyValuePair<string, GroupDefinition> group in _groups) { if (group.Key != text && group.Value.ItemTypes.Contains(itemType)) { dictionary[group.Key] = group.Value.WithoutItem(itemType); } } dictionary[text] = dictionary[text].WithItem(itemType); return new GroupRegistry(dictionary); } public GroupRegistry WithItemUnassigned(string itemType) { Dictionary<string, GroupDefinition> dictionary = new Dictionary<string, GroupDefinition>(_groups); foreach (KeyValuePair<string, GroupDefinition> group in _groups) { if (group.Value.ItemTypes.Contains(itemType)) { dictionary[group.Key] = group.Value.WithoutItem(itemType); } } return new GroupRegistry(dictionary); } public GroupRegistry WithGroupItemsReplaced(string groupName, IEnumerable<string> itemTypes) { string text = GroupNameNormalizer.Normalize(groupName); if (text == null || !_groups.TryGetValue(text, out GroupDefinition value)) { throw new ArgumentException("Group '" + groupName + "' does not exist.", "groupName"); } HashSet<string> hashSet = new HashSet<string>(itemTypes, StringComparer.Ordinal); if (hashSet.Any(string.IsNullOrWhiteSpace)) { throw new ArgumentException("Item types must not be empty.", "itemTypes"); } Dictionary<string, GroupDefinition> dictionary = new Dictionary<string, GroupDefinition>(_groups); foreach (KeyValuePair<string, GroupDefinition> group in _groups) { if (!(group.Key != text)) { continue; } string[] array = group.Value.ItemTypes.Intersect(hashSet).ToArray(); if (array.Length != 0) { GroupDefinition groupDefinition = group.Value; string[] array2 = array; foreach (string itemType in array2) { groupDefinition = groupDefinition.WithoutItem(itemType); } dictionary[group.Key] = groupDefinition; } } dictionary[text] = GroupDefinition.Empty(value.Name, value.CreatorId, value.EvictMismatching, value.Id).WithItems(ItemTypeSet.From(hashSet)); return new GroupRegistry(dictionary); } public GroupRegistry WithEvictMismatching(string groupName, bool value) { string text = GroupNameNormalizer.Normalize(groupName); if (text == null || !_groups.TryGetValue(text, out GroupDefinition value2)) { throw new ArgumentException("Group '" + groupName + "' does not exist.", "groupName"); } if (value2.EvictMismatching != value) { return new GroupRegistry(With(_groups, text, value2.WithEvictMismatching(value))); } return this; } public string? GetGroupForItem(string itemType) { return _groups.Values.FirstOrDefault((GroupDefinition group) => group.ItemTypes.Contains(itemType))?.Name; } private static Dictionary<string, GroupDefinition> With(Dictionary<string, GroupDefinition> groups, string key, GroupDefinition group) { return new Dictionary<string, GroupDefinition>(groups) { [key] = group }; } private static Dictionary<string, GroupDefinition> Without(Dictionary<string, GroupDefinition> groups, string key) { Dictionary<string, GroupDefinition> dictionary = new Dictionary<string, GroupDefinition>(groups); dictionary.Remove(key); return dictionary; } } public enum GroupRegistryMutationKind { CreateGroup, DeleteGroup, RenameGroup, AssignItem, UnassignItem, ReplaceGroupItems, SetGroupEviction } public sealed class GroupRegistryMutation { public GroupRegistryMutationKind Kind { get; } public string? GroupName { get; } public string? NewGroupName { get; } public string? ItemType { get; } public IReadOnlyList<string>? ItemTypes { get; } public bool EvictMismatching { get; } public string? GroupId { get; } public long PlayerId { get; } private GroupRegistryMutation(GroupRegistryMutationKind kind, string? groupName, string? newGroupName, string? itemType, long playerId, IReadOnlyList<string>? itemTypes = null, bool evictMismatching = false, string? groupId = null) { GroupId = groupId; Kind = kind; GroupName = groupName; NewGroupName = newGroupName; ItemType = itemType; ItemTypes = itemTypes; EvictMismatching = evictMismatching; PlayerId = playerId; } public static GroupRegistryMutation CreateGroup(string groupName, long creatorId, string? groupId = null) { return new GroupRegistryMutation(GroupRegistryMutationKind.CreateGroup, groupName, null, null, creatorId, null, evictMismatching: false, groupId); } public static GroupRegistryMutation DeleteGroup(string groupName, long requesterId) { return new GroupRegistryMutation(GroupRegistryMutationKind.DeleteGroup, groupName, null, null, requesterId); } public static GroupRegistryMutation RenameGroup(string oldGroupName, string newGroupName, long requesterId) { return new GroupRegistryMutation(GroupRegistryMutationKind.RenameGroup, oldGroupName, newGroupName, null, requesterId); } public static GroupRegistryMutation AssignItem(string itemType, string groupName) { return new GroupRegistryMutation(GroupRegistryMutationKind.AssignItem, groupName, null, itemType, 0L); } public static GroupRegistryMutation UnassignItem(string itemType) { return new GroupRegistryMutation(GroupRegistryMutationKind.UnassignItem, null, null, itemType, 0L); } public static GroupRegistryMutation ReplaceGroupItems(string groupName, IEnumerable<string> itemTypes, long requesterId) { return new GroupRegistryMutation(GroupRegistryMutationKind.ReplaceGroupItems, groupName, null, null, requesterId, itemTypes.ToArray()); } public static GroupRegistryMutation SetGroupEviction(string groupName, bool value, long requesterId) { return new GroupRegistryMutation(GroupRegistryMutationKind.SetGroupEviction, groupName, null, null, requesterId, null, value); } public GroupRegistryMutation WithVerifiedPlayerId(long verifiedPlayerId) { return new GroupRegistryMutation(Kind, GroupName, NewGroupName, ItemType, verifiedPlayerId, ItemTypes, EvictMismatching, GroupId); } public GroupRegistry Apply(GroupRegistry registry) { return Kind switch { GroupRegistryMutationKind.CreateGroup => registry.WithGroup(GroupName, PlayerId, GroupId), GroupRegistryMutationKind.DeleteGroup => registry.WithoutGroup(GroupName), GroupRegistryMutationKind.RenameGroup => registry.WithRenamedGroup(GroupName, NewGroupName), GroupRegistryMutationKind.AssignItem => registry.WithItemAssignedToGroup(ItemType, GroupName), GroupRegistryMutationKind.UnassignItem => registry.WithItemUnassigned(ItemType), GroupRegistryMutationKind.ReplaceGroupItems => registry.WithGroupItemsReplaced(GroupName, ItemTypes), GroupRegistryMutationKind.SetGroupEviction => registry.WithEvictMismatching(GroupName, EvictMismatching), _ => throw new InvalidOperationException($"Unknown mutation kind '{Kind}'."), }; } } public sealed class ItemPinCandidates { public IReadOnlyList<string> InChest { get; } public IReadOnlyList<string> CarriedOnly { get; } private ItemPinCandidates(IReadOnlyList<string> inChest, IReadOnlyList<string> carriedOnly) { InChest = inChest; CarriedOnly = carriedOnly; } public static ItemPinCandidates Build(IEnumerable<string> chestItemTypes, IEnumerable<string> carriedItemTypes) { List<string> list = DistinctSorted(chestItemTypes); HashSet<string> inChestSet = new HashSet<string>(list, StringComparer.Ordinal); List<string> carriedOnly = (from itemType in DistinctSorted(carriedItemTypes) where !inChestSet.Contains(itemType) select itemType).ToList(); return new ItemPinCandidates(list, carriedOnly); } private static List<string> DistinctSorted(IEnumerable<string> itemTypes) { return itemTypes.Where((string itemType) => !string.IsNullOrEmpty(itemType)).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string itemType) => itemType, StringComparer.Ordinal) .ToList(); } } public sealed class ItemTypeSet : IReadOnlyCollection<string>, IEnumerable<string>, IEnumerable { private readonly HashSet<string> _items; public static ItemTypeSet Empty { get; } = new ItemTypeSet(new HashSet<string>(StringComparer.Ordinal)); public int Count => _items.Count; private ItemTypeSet(HashSet<string> items) { _items = items; } public bool Contains(string itemType) { return _items.Contains(itemType); } internal ItemTypeSet Add(string itemType) { if (_items.Contains(itemType)) { return this; } return new ItemTypeSet(new HashSet<string>(_items, StringComparer.Ordinal) { itemType }); } internal ItemTypeSet Remove(string itemType) { if (!_items.Contains(itemType)) { return this; } HashSet<string> hashSet = new HashSet<string>(_items, StringComparer.Ordinal); hashSet.Remove(itemType); return new ItemTypeSet(hashSet); } internal static ItemTypeSet From(IEnumerable<string> itemTypes) { return new ItemTypeSet(new HashSet<string>(itemTypes, StringComparer.Ordinal)); } public IEnumerator<string> GetEnumerator() { return _items.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }
plugins/StorageGroups.dll
Decompiled 16 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.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SmartContainerRouting.Core.Model; using SmartContainerRouting.Core.Persistence; using SmartContainerRouting.Core.Routing; using SmartContainerRouting.Core.Text; using SmartContainerRouting.Plugin.Persistence; using SmartContainerRouting.Plugin.Routing; using SmartContainerRouting.Plugin.Ui; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("jonasd")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+f556d01ba578811190b24467d0afe74ca948dcb8")] [assembly: AssemblyProduct("Storage Groups")] [assembly: AssemblyTitle("StorageGroups")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SmartContainerRouting.Plugin { [BepInPlugin("com.jonasd.valheim.storagegroups", "Storage Groups", "0.1.7")] [BepInIncompatibility("flueno.SmartContainers")] public sealed class SmartContainerRoutingPlugin : BaseUnityPlugin { public const string PluginGuid = "com.jonasd.valheim.storagegroups"; public const string PluginName = "Storage Groups"; public const string PluginVersion = "0.1.7"; private const string SmartContainersGuid = "flueno.SmartContainers"; private Harmony? _harmony; internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("com.jonasd.valheim.storagegroups"); _harmony.PatchAll(); if (Localization.instance != null) { LocalizationPatch.AddWord(Localization.instance, "scr_container_name", "Container name"); LocalizationPatch.AddWord(Localization.instance, "scr_group_name", "Group name"); } RoutingConfig.RangeMeters = ((BaseUnityPlugin)this).Config.Bind<float>("Routing", "RangeMeters", 10f, "Maximum distance, in meters, between a container that just received an item and an eligible destination container for the item to route there.").Value; RoutingConfig.ShowRoutingMessages = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowRoutingMessages", false, "Show a short message for every item routed near you, and log the details. Also toggled from the Storage Groups menu."); GroupRegistryAccess.AnchorZdoProvider = ModDataAnchorProvider.GetOrCreate; ((Component)this).gameObject.AddComponent<GroupManagementController>(); ((Component)this).gameObject.AddComponent<GroupItemsController>(); } private void Update() { ModDataAnchorProvider.GetOrCreate(); ContainerRoutingService.ObserveGroupRegistry(); ContainerRoutingService.ProcessPendingInventoryChanges(); RouteRelay.Tick(); RoutingNotifier.Flush(); } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace SmartContainerRouting.Plugin.Ui { [HarmonyPatch(typeof(Container), "Awake")] internal static class ContainerAwakePatch { private static void Postfix(Container __instance) { if (ContainerKindFilter.IsRoutable(__instance) && (Object)(object)((Component)__instance).GetComponent<ContainerRoutingBehaviour>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<ContainerRoutingBehaviour>(); } } } [HarmonyPatch(typeof(Container), "GetHoverText")] internal static class ContainerHoverTextPatch { private static void Postfix(Container __instance, ref string __result) { ContainerRoutingBehaviour component = ((Component)__instance).GetComponent<ContainerRoutingBehaviour>(); if (!((Object)(object)component == (Object)null)) { string text = ContainerHoverText.BuildExtraLine(component.GetSettings(), (Func<string, string>)GroupRegistryAccess.DisplayName, (Func<string, bool>)GroupRegistryAccess.IsEvicting, ContainerAccessGuard.GetCoveringWardIds(__instance).Count > 0); if (!string.IsNullOrEmpty(text)) { __result = __result + "\n" + text; } } } } [HarmonyPatch(typeof(Container), "Load")] internal static class ContainerLoadPatch { public static bool IsLoading { get; private set; } private static void Prefix() { IsLoading = true; } private static void Postfix(Container __instance, bool __result) { IsLoading = false; ContainerRoutingBehaviour component = ((Component)__instance).GetComponent<ContainerRoutingBehaviour>(); if ((Object)(object)component == (Object)null) { return; } component.RefreshKnownItems(); if (__result) { component.MarkRoutingReady(); if (GroupRegistryAccess.TryLoad(out GroupRegistry registry)) { component.ReconcileGroup(registry); } } } private static Exception Finalizer(Exception __exception) { IsLoading = false; return __exception; } } internal sealed class ContainerPanelController : MonoBehaviour { private static ContainerPanelController? _instance; private const float TopInset = 8f; private const float Gap = 8f; private const float ColumnWidth = 140f; private const float ControlHeight = 24f; private const float PanelArtOverhang = 10f; private static readonly string[] PanelArtPaths = new string[2] { "Bkg", "selected_frame/selected (1)" }; private static readonly List<RectTransform> ScratchRects = new List<RectTransform>(); private static readonly Vector3[] ScratchCorners = (Vector3[])(object)new Vector3[4]; private const int SettleFrames = 10; private RectTransform? _row; private TMP_Text? _groupButtonLabel; private Button? _newGroupButton; private Button? _addButton; private Button? _editItemsButton; private Toggle? _pauseToggle; private InventoryGui? _gui; private ContainerRoutingBehaviour? _current; private ContainerSettings? _lastRefreshedSettings; private string? _lastRefreshedGroupDisplayName; private bool _layoutDirty; private int _framesSinceShown; public static ContainerRoutingBehaviour? Current { get { if (!((Object)(object)_instance != (Object)null)) { return null; } return _instance._current; } } public static void ShowFor(InventoryGui gui, Container container) { if (!ContainerKindFilter.IsRoutable(container)) { _instance?.Hide(); return; } ContainerRoutingBehaviour containerRoutingBehaviour = ((Component)container).GetComponent<ContainerRoutingBehaviour>(); if ((Object)(object)containerRoutingBehaviour == (Object)null) { containerRoutingBehaviour = ((Component)container).gameObject.AddComponent<ContainerRoutingBehaviour>(); } ContainerPanelController orCreate = GetOrCreate(gui); if (orCreate._current != containerRoutingBehaviour) { orCreate.ReleaseHold(); } orCreate._gui = gui; orCreate._current = containerRoutingBehaviour; orCreate._framesSinceShown = 0; RectTransform? row = orCreate._row; if (row != null) { ((Component)row).gameObject.SetActive(true); } orCreate.Refresh(); orCreate.PositionRow(); } private void Hide() { ReleaseHold(); _current = null; RectTransform? row = _row; if (row != null) { ((Component)row).gameObject.SetActive(false); } } private static ContainerPanelController GetOrCreate(InventoryGui gui) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00c7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("SCR_ContainerControls", new Type[3] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter) }); val.transform.SetParent((Transform)(object)gui.m_container, false); ContentSizeFitter component = val.GetComponent<ContentSizeFitter>(); component.horizontalFit = (FitMode)0; component.verticalFit = (FitMode)2; RectTransform component2 = val.GetComponent<RectTransform>(); component2.anchorMin = new Vector2(1f, 1f); component2.anchorMax = new Vector2(1f, 1f); component2.pivot = new Vector2(0f, 1f); component2.sizeDelta = new Vector2(140f, component2.sizeDelta.y); _instance = val.AddComponent<ContainerPanelController>(); _instance._row = component2; _instance.Build(val.GetComponent<VerticalLayoutGroup>()); return _instance; } private void Build(VerticalLayoutGroup layout) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Expected O, but got Unknown ((HorizontalOrVerticalLayoutGroup)layout).spacing = 4f; ((HorizontalOrVerticalLayoutGroup)layout).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)layout).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)layout).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)layout).childForceExpandHeight = false; ((LayoutGroup)layout).childAlignment = (TextAnchor)0; Button val = UiFactory.CreateButton(((Component)this).transform, string.Empty, (UnityAction)delegate { GroupManagementController.Toggle(_current); }); _groupButtonLabel = ((Component)val).GetComponentInChildren<TMP_Text>(); ((Component)val).gameObject.AddComponent<HoverTooltip>().Initialize(DescribeGroupButton); _newGroupButton = UiFactory.CreateButton(((Component)this).transform, "New Group From Contents", new UnityAction(OnNewGroupClicked)); ((Component)_newGroupButton).gameObject.SetActive(false); ((Component)_newGroupButton).gameObject.AddComponent<HoverTooltip>().Initialize(delegate { ContainerRoutingBehaviour? current = _current; if (current != null) { Inventory? inventory = current.GetInventory(); if (((inventory != null) ? new int?(inventory.GetAllItems().Count) : ((int?)null)) > 0) { return "Makes a group from this chest's items\nand puts this chest in it."; } } return "Put items in the chest first."; }); _addButton = UiFactory.CreateButton(((Component)this).transform, "Add Contents", new UnityAction(OnAddContentsClicked)); ((Component)_addButton).gameObject.SetActive(false); ((Component)_addButton).gameObject.AddComponent<GroupContentsTooltip>().Initialize(delegate { ContainerRoutingBehaviour? current = _current; return (current == null) ? null : current.GetSettings().GroupName; }, null, "Adds this chest's items to the group"); _editItemsButton = UiFactory.CreateButton(((Component)this).transform, "Edit Items...", (UnityAction)delegate { GroupItemsController.Toggle(_current); }); ((Component)_editItemsButton).gameObject.SetActive(false); _pauseToggle = UiFactory.CreateToggle(((Component)this).transform, "Pause sorting", initialValue: false, delegate(bool value) { _current?.SetHeld(value); }); ((Component)_pauseToggle).gameObject.AddComponent<HoverTooltip>().Initialize(() => "Nothing moves in or out\nof this chest while ticked.\nTurns off when you close it."); LayoutElement[] componentsInChildren = ((Component)this).GetComponentsInChildren<LayoutElement>(true); for (int num = 0; num < componentsInChildren.Length; num++) { componentsInChildren[num].preferredHeight = 24f; } } private void PositionRow() { //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_0057: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_row == (Object)null || (Object)(object)_gui == (Object)null) { return; } RectTransform container = _gui.m_container; if (container == null) { return; } if (_layoutDirty || _framesSinceShown <= 10) { LayoutRebuilder.ForceRebuildLayoutImmediate(_row); _layoutDirty = false; } Vector3 lossyScale = ((Transform)container).lossyScale; if (lossyScale.x <= 0f || lossyScale.y <= 0f) { return; } (float left, float right, float bottom, float top) tuple = WorldBounds(container, includeChildren: false); float item = tuple.right; float item2 = tuple.top; float num = 8f * lossyScale.x; float num2 = 8f * lossyScale.y; float num3 = Mathf.Max(Mathf.Max(Mathf.Max(PanelArtRightEdge(container, item, lossyScale.x), TitleRightEdge(_gui.m_containerName)), RightEdge((Component?)(object)_gui.m_takeAllButton)), RightEdge((Component?)(object)_gui.m_stackAllButton)) + num; float num4 = item2 - 8f * lossyScale.y; Rect rect = _row.rect; float num5 = num4 - ((Rect)(ref rect)).height * lossyScale.y; float num6 = num3 + 140f * lossyScale.x; if ((Object)(object)_gui.m_containerWeight != (Object)null && ((Component)_gui.m_containerWeight).gameObject.activeInHierarchy) { Transform parent = _gui.m_containerWeight.transform.parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); var (num7, num8, _, num9) = WorldBounds(((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)container) ? val : _gui.m_containerWeight.rectTransform, includeChildren: true); if (num3 < num8 && num7 < num6 && num5 < num9 + num2) { num3 = Mathf.Max(num3, num8 + num); } } _row.anchoredPosition = new Vector2((num3 - item) / lossyScale.x, -8f); } private static float PanelArtRightEdge(RectTransform panel, float panelRight, float scaleX) { float num = panelRight + 10f * scaleX; string[] panelArtPaths = PanelArtPaths; foreach (string text in panelArtPaths) { num = Mathf.Max(num, RightEdge((Component?)(object)((Transform)panel).Find(text))); } return num; } private static float RightEdge(Component? element) { if ((Object)(object)element != (Object)null && element.gameObject.activeInHierarchy) { Transform transform = element.transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (val != null) { return WorldBounds(val, includeChildren: true).right; } } return float.NegativeInfinity; } private static float TitleRightEdge(TMP_Text? title) { //IL_002f: 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_0037: 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_0058: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)title == (Object)null || !((Component)title).gameObject.activeInHierarchy) { return float.NegativeInfinity; } float item = WorldBounds(title.rectTransform, includeChildren: false).right; Bounds textBounds = title.textBounds; if (!(((Bounds)(ref textBounds)).size.x > 0f)) { return item; } return Mathf.Max(item, title.transform.TransformPoint(((Bounds)(ref textBounds)).max).x); } private static (float left, float right, float bottom, float top) WorldBounds(RectTransform rect, bool includeChildren) { rect.GetWorldCorners(ScratchCorners); float num = ScratchCorners[0].x; float num2 = ScratchCorners[0].y; float num3 = ScratchCorners[2].x; float num4 = ScratchCorners[2].y; if (!includeChildren) { return (left: num, right: num3, bottom: num2, top: num4); } ((Component)rect).GetComponentsInChildren<RectTransform>(false, ScratchRects); foreach (RectTransform scratchRect in ScratchRects) { scratchRect.GetWorldCorners(ScratchCorners); num = Mathf.Min(num, ScratchCorners[0].x); num2 = Mathf.Min(num2, ScratchCorners[0].y); num3 = Mathf.Max(num3, ScratchCorners[2].x); num4 = Mathf.Max(num4, ScratchCorners[2].y); } ScratchRects.Clear(); return (left: num, right: num3, bottom: num2, top: num4); } private void OnDisable() { ReleaseHold(); } private void ReleaseHold() { _current?.SetHeld(held: false); Toggle? pauseToggle = _pauseToggle; if (pauseToggle != null) { pauseToggle.SetIsOnWithoutNotify(false); } } private string? DescribeGroupButton() { ContainerRoutingBehaviour? current = _current; string text = ((current != null) ? current.GetSettings().GroupName : null); if (text != null && GroupRegistryAccess.TryLoad(out GroupRegistry registry)) { GroupDefinition val = registry.GetGroup(text); if (val != null) { string text2 = GroupButtonHover.Describe(val.Name, ((IEnumerable<string>)val.ItemTypes).Select(ItemTypeResolver.GetDisplayName)); string text3 = DescribeRoutes(); return text2 + ((text3 != null) ? ("\n\n" + text3) : string.Empty); } } string text4 = GroupButtonHover.Describe((string)null, Enumerable.Empty<string>()); string text5 = DescribeRoutes(); return text4 + ((text5 != null) ? ("\n\n" + text5) : string.Empty); } private string? DescribeRoutes() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown ContainerRoutingBehaviour current = _current; if ((Object)(object)current == (Object)null || !current.IsValid) { return null; } if (!GroupRegistryAccess.TryLoad(out GroupRegistry registry)) { return "Groups not loaded yet"; } Position position = current.GetPosition(); List<Neighbour> list = new List<Neighbour>(); foreach (ContainerRoutingBehaviour item in ContainerRegistry.All) { if (!((Object)(object)item == (Object)null) && item != current && item.IsValid && item.IsEligibleRoutingDestination && !item.IsHeld) { ContainerRoutingCandidate val = item.ToCandidate(); list.Add(new Neighbour(val.Settings, ((Position)(ref position)).DistanceTo(val.Position), val.FreeCapacity > 0 || val.PartialStackCapacity.Values.Any((int space) => space > 0), current.CanRouteTo(item))); } } return RouteSummary.Describe(current.GetSettings(), registry, (IEnumerable<Neighbour>)list, RoutingConfig.RangeMeters, current.IsHeld); } private void OnNewGroupClicked() { ContainerRoutingBehaviour current = _current; if ((Object)(object)current == (Object)null || !GroupRegistryAccess.TryLoad(out GroupRegistry registry)) { return; } Inventory? inventory = current.GetInventory(); List<ItemData> source = ((inventory != null) ? inventory.GetAllItems() : null) ?? new List<ItemData>(); string text = GroupNameSuggestion.Suggest(source.Select((ItemData item) => new KeyValuePair<string, int>(ItemTypeResolver.GetDisplayName(ItemTypeResolver.GetItemType(item)), item.m_stack)), registry); if (text == null) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "Put items in the chest first", 0, (Sprite)null, false); } } else { IReadOnlyList<string> inChest = ItemPinCandidates.Build(source.Select(ItemTypeResolver.GetItemType), Enumerable.Empty<string>()).InChest; GroupManagementController.NewGroupFromContents(current, text, inChest); } } private static void SetShown(Button? button, bool shown, bool interactable) { if (!((Object)(object)button == (Object)null)) { if (((Component)button).gameObject.activeSelf != shown) { ((Component)button).gameObject.SetActive(shown); } ((Selectable)button).interactable = interactable; } } private void OnAddContentsClicked() { ContainerRoutingBehaviour current = _current; string text = ((current != null) ? current.GetSettings().GroupName : null); if ((Object)(object)current == (Object)null || text == null || !GroupRegistryAccess.TryLoad(out GroupRegistry registry)) { return; } Inventory? inventory = current.GetInventory(); IEnumerable<string> enumerable = ((inventory != null) ? inventory.GetAllItems().Select(ItemTypeResolver.GetItemType) : null) ?? Enumerable.Empty<string>(); IReadOnlyList<string> readOnlyList = GroupItemAdditions.Missing(registry, text, enumerable); if (readOnlyList.Count == 0) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, "Everything in this chest is already in the group", 0, (Sprite)null, false); } return; } string text2 = GroupItemTransfers.Describe(registry, text, (IEnumerable<string>)readOnlyList, (Func<string, string>)ItemTypeResolver.GetDisplayName); foreach (string item in readOnlyList) { GroupRegistryRpc.Request(GroupRegistryMutation.AssignItem(item, text)); } Button? addButton = _addButton; if (addButton != null) { ((Component)addButton).GetComponent<GroupContentsTooltip>()?.Refresh(); } if (text2 != null) { Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, text2, 0, (Sprite)null, false); } } } private void Refresh() { if ((Object)(object)_current == (Object)null) { return; } ContainerSettings settings = _current.GetSettings(); string text = GroupRegistryAccess.DisplayName(settings.GroupName); bool flag = settings.GroupName != null; GroupRegistry registry; bool interactable = flag && (Object)(object)Player.m_localPlayer != (Object)null && GroupRegistryAccess.TryLoad(out registry) && registry.HasGroup(settings.GroupName); Button? newGroupButton = _newGroupButton; bool shown = !flag; int interactable2; if ((Object)(object)Player.m_localPlayer != (Object)null) { Inventory? inventory = _current.GetInventory(); if (inventory != null && inventory.GetAllItems().Count > 0) { interactable2 = (GroupRegistryAccess.TryLoad(out GroupRegistry _) ? 1 : 0); goto IL_00a0; } } interactable2 = 0; goto IL_00a0; IL_00a0: SetShown(newGroupButton, shown, (byte)interactable2 != 0); SetShown(_addButton, flag, interactable); SetShown(_editItemsButton, flag, interactable); if (!settings.Equals(_lastRefreshedSettings) || !(text == _lastRefreshedGroupDisplayName)) { _lastRefreshedSettings = settings; _lastRefreshedGroupDisplayName = text; _layoutDirty = true; if ((Object)(object)_groupButtonLabel != (Object)null) { _groupButtonLabel.text = "Group: " + (text ?? "(none)"); } } } private void Update() { Refresh(); _framesSinceShown++; PositionRow(); } } public sealed class ContainerRoutingBehaviour : MonoBehaviour { private sealed class ReferenceItemComparer : IEqualityComparer<ItemData> { public static readonly ReferenceItemComparer Instance = new ReferenceItemComparer(); public bool Equals(ItemData x, ItemData y) { return x == y; } public int GetHashCode(ItemData obj) { return RuntimeHelpers.GetHashCode(obj); } } private static readonly MethodInfo ContainerLoadMethod = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null); private ZNetView? _nview; private Container? _container; private List<ItemData> _knownItems = new List<ItemData>(); private List<ItemTypeQuantity> _knownStacks = new List<ItemTypeQuantity>(); private ContainerRoutingCandidate? _knownCapacity; private bool _wasOwner; private Dictionary<ItemData, int> _knownStackSizes = new Dictionary<ItemData, int>(ReferenceItemComparer.Instance); public bool IsValid { get { if ((Object)(object)_nview != (Object)null) { return _nview.IsValid(); } return false; } } public bool IsOwner { get { if ((Object)(object)_nview != (Object)null) { return _nview.IsOwner(); } return false; } } public bool IsEligibleRoutingDestination { get { if ((Object)(object)_container != (Object)null) { return ContainerAccessGuard.IsEligibleRoutingDestination(_container); } return false; } } public bool IsRoutingReady { get; private set; } public bool IsHeld { get; private set; } public string RoutingId { get { if (!IsValid) { return ((Object)this).GetInstanceID().ToString(); } return ((object)Unsafe.As<ZDOID, ZDOID>(ref _nview.GetZDO().m_uid)/*cast due to .constrained prefix*/).ToString(); } } public ZDOID? ZdoId { get { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!IsValid) { return null; } return _nview.GetZDO().m_uid; } } private void Awake() { _container = ((Component)this).GetComponent<Container>(); _nview = (((Object)(object)_container != (Object)null && (Object)(object)_container.m_rootObjectOverride != (Object)null) ? _container.m_rootObjectOverride : ((Component)this).GetComponent<ZNetView>()); _wasOwner = IsOwner; ContainerRegistry.Register(this); if ((Object)(object)_nview != (Object)null && _nview.IsValid()) { RouteRelay.RegisterChest(_nview, this); } Inventory inventory = GetInventory(); if (inventory != null) { SnapshotKnownItems(inventory.GetAllItems()); _knownCapacity = ToCandidate(); inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, new Action(OnInventoryChanged)); } } private void OnDestroy() { ContainerRegistry.Unregister(this); Inventory inventory = GetInventory(); if (inventory != null) { inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, new Action(OnInventoryChanged)); } } internal void RefreshIfOwnershipJustClaimed() { bool isOwner = IsOwner; if (isOwner && !_wasOwner && (Object)(object)_container != (Object)null) { ContainerLoadMethod.Invoke(_container, null); } _wasOwner = isOwner; } public List<string> CoveringWardIds() { if (!((Object)(object)_container != (Object)null)) { return new List<string>(); } return ContainerAccessGuard.GetCoveringWardIds(_container); } public bool CanRouteTo(ContainerRoutingBehaviour destination) { if ((Object)(object)_container != (Object)null && (Object)(object)destination._container != (Object)null) { return ContainerAccessGuard.CanRouteBetween(_container, destination._container); } return false; } public void SetHeld(bool held) { if (IsHeld != held) { IsHeld = held; if (!held) { ContainerRoutingService.QueueReroute(this); ContainerRoutingService.QueueCapacityAvailable(this); } } } internal void MarkRoutingReady() { IsRoutingReady = true; } public Inventory? GetInventory() { if (!((Object)(object)_container != (Object)null)) { return null; } return _container.GetInventory(); } public Position GetPosition() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) Vector3 val = (IsValid ? _nview.GetZDO().GetPosition() : ((Component)this).transform.position); return new Position(val.x, val.y, val.z); } public ContainerRoutingCandidate ToCandidate() { //IL_0084: 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_00a2: Expected O, but got Unknown Inventory inventory = GetInventory(); Dictionary<string, int> dictionary = new Dictionary<string, int>(); if (inventory != null) { foreach (ItemData allItem in inventory.GetAllItems()) { int num = allItem.m_shared.m_maxStackSize - allItem.m_stack; if (num > 0) { string itemType = ItemTypeResolver.GetItemType(allItem); dictionary[itemType] = (dictionary.TryGetValue(itemType, out var value) ? (value + num) : num); } } } return new ContainerRoutingCandidate(RoutingId, GetPosition(), (inventory != null) ? inventory.GetEmptySlots() : 0, GetSettings(), (IReadOnlyDictionary<string, int>)dictionary); } public void RefreshKnownItems() { Inventory inventory = GetInventory(); SnapshotKnownItems((inventory != null) ? inventory.GetAllItems() : new List<ItemData>()); _knownCapacity = ToCandidate(); } private void SnapshotKnownItems(List<ItemData> items) { _knownItems = new List<ItemData>(items); _knownStacks = items.Select(ToStack).ToList(); _knownStackSizes = new Dictionary<ItemData, int>(ReferenceItemComparer.Instance); foreach (ItemData item in items) { _knownStackSizes[item] = item.m_stack; } } private void OnInventoryChanged() { try { if (!ContainerRoutingService.IsRouting && !ContainerLoadPatch.IsLoading) { ContainerRoutingService.QueueInventoryChanged(this); } HandleInventoryChanged(); } catch (Exception arg) { SmartContainerRoutingPlugin.Log.LogError((object)$"Inventory-change routing hook threw and was suppressed to avoid corrupting the in-flight inventory operation: {arg}"); } } private void HandleInventoryChanged() { if (ContainerRoutingService.IsRouting || ContainerLoadPatch.IsLoading) { return; } Inventory inventory = GetInventory(); if (inventory == null) { return; } List<ItemData> allItems = inventory.GetAllItems(); ContainerRoutingCandidate val = ToCandidate(); bool flag = _knownCapacity != null && val.HasMoreCapacityThan(_knownCapacity); List<ItemData> list = allItems.Where((ItemData item2) => !_knownItems.Contains(item2)).ToList(); List<ItemTypeQuantity> knownStacks = _knownStacks; List<ItemTypeQuantity> list2 = allItems.Select(ToStack).ToList(); List<(ItemData, int)> list3 = new List<(ItemData, int)>(); foreach (ItemData item2 in allItems) { if (_knownStackSizes.TryGetValue(item2, out var value) && item2.m_stack > value) { list3.Add((item2, item2.m_stack - value)); } } SnapshotKnownItems(allItems); _knownCapacity = val; Dictionary<string, int> dictionary = NewArrivalFilter.ComputeBudget((IReadOnlyList<ItemTypeQuantity>)knownStacks, (IReadOnlyList<ItemTypeQuantity>)list2); IReadOnlyList<ItemData> readOnlyList = NewArrivalFilter.SelectWithinBudget<ItemData>(dictionary, (IReadOnlyList<ItemData>)list, (Func<ItemData, ItemTypeQuantity>)ToStack); IReadOnlyList<(ItemData, int)> readOnlyList2 = NewArrivalFilter.SelectWithinBudget<(ItemData, int)>(dictionary, (IReadOnlyList<(ItemData, int)>)list3, (Func<(ItemData, int), ItemTypeQuantity>)(((ItemData Item, int Delta) growth) => new ItemTypeQuantity(ItemTypeResolver.GetItemType(growth.Item), growth.Delta))); foreach (ItemData item3 in readOnlyList) { ContainerRoutingService.TryRoute(this, item3, item3.m_stack); } foreach (var (item, amount) in readOnlyList2) { ContainerRoutingService.TryRoute(this, item, amount); } if (flag) { ContainerRoutingService.QueueCapacityAvailable(this); } } private static ItemTypeQuantity ToStack(ItemData item) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown return new ItemTypeQuantity(ItemTypeResolver.GetItemType(item), item.m_stack); } public ContainerSettings GetSettings() { if (!IsValid) { return ContainerSettings.Default; } return ContainerZdoStore.Load(_nview.GetZDO()); } public void SaveSettings(ContainerSettings settings, string? knownGroupId = null) { if (ContainerOwnershipGuard.CanWrite(_nview)) { string groupName = GetSettings().GroupName; ContainerZdoStore.Save(_nview.GetZDO(), settings, knownGroupId); if (groupName != settings.GroupName) { ContainerRoutingService.QueueReroute(this); ContainerRoutingService.QueueCapacityAvailable(this); } } else if (IsValid) { GroupRegistry registry; string text = GroupReference.IdFor(settings.GroupName, GroupRegistryAccess.TryLoad(out registry) ? registry : null); RouteRelay.RequestSettings(this, settings, (text.Length > 0) ? text : (knownGroupId ?? string.Empty)); } } public void SetGroup(string? groupName, string? knownGroupId = null) { SaveSettings(GetSettings().WithGroupName(groupName), knownGroupId); } public void ReconcileGroup(GroupRegistry groups) { if (IsOwner) { string groupName = GetSettings().GroupName; if (groupName != null && !groups.HasGroup(groupName)) { SetGroup(null); } else if (groupName != null && ContainerZdoStore.LacksGroupId(_nview.GetZDO())) { SetGroup(groupName); } } } } internal sealed class DelegateTextReceiver : TextReceiver { private readonly Func<string> _getText; private readonly Action<string> _setText; public DelegateTextReceiver(Func<string> getText, Action<string> setText) { _getText = getText; _setText = setText; } public string GetText() { return _getText(); } public void SetText(string text) { _setText(text); } } internal sealed class GroupContentsTooltip : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler { private const int VisibleItemLimit = 8; private const float RegistryCheckIntervalSeconds = 0.1f; private bool _hovered; private string? _lastRegistryBlob; private float _nextRegistryCheck; private Func<string?>? _groupName; private Transform? _displayParent; private GameObject? _panel; private string? _actionHint; public void Initialize(Func<string?> groupName, Transform? displayParent = null, string? actionHint = null) { _groupName = groupName; _displayParent = displayParent; _actionHint = actionHint; } public void OnPointerEnter(PointerEventData eventData) { _hovered = true; Refresh(); } public void OnPointerExit(PointerEventData eventData) { _hovered = false; Hide(); } private void OnDisable() { _hovered = false; Hide(); } private void Update() { if (_hovered && !(Time.unscaledTime < _nextRegistryCheck)) { _nextRegistryCheck = Time.unscaledTime + 0.1f; if (GroupRegistryAccess.CurrentBlob() != _lastRegistryBlob) { Refresh(); } } } public void Refresh() { //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) Hide(); _lastRegistryBlob = GroupRegistryAccess.CurrentBlob(); string text = _groupName?.Invoke(); if (text == null || !GroupRegistryAccess.TryLoad(out GroupRegistry registry)) { return; } GroupDefinition val = registry.GetGroup(text); if (val == null) { return; } string[] array = ((IEnumerable<string>)val.ItemTypes).OrderBy<string, string>((string item) => item, StringComparer.Ordinal).ToArray(); string[] array2 = array.Take(8).ToArray(); int num = array.Length - array2.Length; string[] array3 = ((array2.Length != 0) ? array2 : new string[1] { "(empty)" }); string text2 = val.Name + "\n" + ((_actionHint == null) ? string.Empty : (_actionHint + "\n")) + array.Length + " current item types:\n" + string.Join("\n", array3); if (num > 0) { text2 = text2 + "\n+" + num + " more"; } int num2 = array3.Length + 2 + ((_actionHint != null) ? 1 : 0) + ((num > 0) ? 1 : 0); float num3 = 20f + (float)num2 * 19f; _panel = UiFactory.CreatePanel(_displayParent ?? ((Component)this).transform, "SCR_GroupContentsTooltip", new Vector2(220f, num3)); RectTransform component = _panel.GetComponent<RectTransform>(); component.pivot = new Vector2(1f, 1f); if ((Object)(object)_displayParent == (Object)null) { component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(0f, 1f); component.anchoredPosition = new Vector2(-8f, 0f); } else { Transform transform = ((Component)this).transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (val2 != null) { Rect rect = val2.rect; float num4 = ((Rect)(ref rect)).xMin - 8f; rect = val2.rect; ((Transform)component).position = ((Transform)val2).TransformPoint(new Vector3(num4, ((Rect)(ref rect)).yMax, 0f)); } } ((Graphic)_panel.GetComponent<Image>()).raycastTarget = false; TMP_Text obj = UiFactory.CreateText(_panel.transform, text2); obj.alignment = (TextAlignmentOptions)257; obj.fontSize = 14f; obj.textWrappingMode = (TextWrappingModes)0; obj.overflowMode = (TextOverflowModes)1; ((Graphic)obj).raycastTarget = false; ((Component)obj).gameObject.AddComponent<LayoutElement>().preferredHeight = num3 - 20f; } private void Hide() { if ((Object)(object)_panel != (Object)null) { _panel.SetActive(false); Object.Destroy((Object)(object)_panel); _panel = null; } } } internal sealed class GroupEvictionTooltip : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler { private const string Explanation = "Strict: only group items\n\nApplies to every chest in this group. Items that aren't in the group move to a nearby chest with room. A chest in the item's group is tried first, even if that group also evicts. Otherwise, the nearest chest that isn't strict is used. A strict chest only receives its group's items."; private Transform? _displayParent; private GameObject? _panel; public void Initialize(Transform? displayParent) { _displayParent = displayParent; } public void OnPointerEnter(PointerEventData eventData) { //IL_003a: 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_005c: 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_0070: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) Hide(); if ((Object)(object)_displayParent == (Object)null) { return; } Transform transform = ((Component)this).transform; RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (val != null) { _panel = UiFactory.CreatePanel(_displayParent, "SCR_GroupEvictionTooltip", new Vector2(320f, 195f)); RectTransform component = _panel.GetComponent<RectTransform>(); Rect rect = val.rect; float num = ((Rect)(ref rect)).xMax + 8f; rect = val.rect; Vector3 val2 = ((Transform)val).TransformPoint(new Vector3(num, ((Rect)(ref rect)).yMax, 0f)); if (val2.x + 320f <= (float)Screen.width) { component.pivot = new Vector2(0f, 1f); ((Transform)component).position = val2; } else { component.pivot = Vector2.one; rect = val.rect; float num2 = ((Rect)(ref rect)).xMin - 8f; rect = val.rect; ((Transform)component).position = ((Transform)val).TransformPoint(new Vector3(num2, ((Rect)(ref rect)).yMax, 0f)); } ((Graphic)_panel.GetComponent<Image>()).raycastTarget = false; TMP_Text obj = UiFactory.CreateText(_panel.transform, "Strict: only group items\n\nApplies to every chest in this group. Items that aren't in the group move to a nearby chest with room. A chest in the item's group is tried first, even if that group also evicts. Otherwise, the nearest chest that isn't strict is used. A strict chest only receives its group's items."); obj.alignment = (TextAlignmentOptions)257; obj.fontSize = 16f; obj.textWrappingMode = (TextWrappingModes)1; obj.overflowMode = (TextOverflowModes)1; ((Graphic)obj).raycastTarget = false; ((Component)obj).gameObject.AddComponent<LayoutElement>().preferredHeight = 175f; } } public void OnPointerExit(PointerEventData eventData) { Hide(); } private void OnDisable() { Hide(); } private void Hide() { if ((Object)(object)_panel != (Object)null) { _panel.SetActive(false); Object.Destroy((Object)(object)_panel); _panel = null; } } } internal sealed class GroupItemsController : MonoBehaviour { private const float PanelWidth = 360f; private const float RowHeight = 32f; private const float MaxListHeight = 360f; private const float RegistryCheckIntervalSeconds = 0.1f; private const float ClearConfirmSeconds = 3f; private static GroupItemsController? _instance; private ContainerRoutingBehaviour? _source; private Transform? _canvasTransform; private GameObject? _panel; private string? _lastRegistryBlob; private float _nextRegistryCheck; private float _clearConfirmUntil; private void Awake() { _instance = this; } private void OnDestroy() { if (_instance == this) { _instance = null; } } public static void Toggle(ContainerRoutingBehaviour? source) { if (!((Object)(object)_instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null)) { if ((Object)(object)_instance._panel != (Object)null) { _instance.Close(); } else if (source != null && source.GetSettings().GroupName != null) { GroupManagementController.CloseIfOpen(); _instance.Open(source); } } } public static void CloseIfOpen() { if ((Object)(object)_instance != (Object)null && (Object)(object)_instance._panel != (Object)null) { _instance.Close(); } } private void Update() { if ((Object)(object)_panel == (Object)null) { return; } if (!IsSourceChestStillOpen()) { Close(); return; } if (_clearConfirmUntil > 0f && Time.unscaledTime >= _clearConfirmUntil) { _clearConfirmUntil = 0f; Rebuild(); } if (Time.unscaledTime >= _nextRegistryCheck) { _nextRegistryCheck = Time.unscaledTime + 0.1f; string text = GroupRegistryAccess.CurrentBlob(); if (text != null && text != _lastRegistryBlob) { Rebuild(); } } } private bool IsSourceChestStillOpen() { if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)_source != (Object)null && InventoryGui.IsVisible() && (Object)(object)InventoryGui.instance != (Object)null && InventoryGui.instance.IsContainerOpen()) { return (Object)(object)ContainerPanelController.Current == (Object)(object)_source; } return false; } private void Open(ContainerRoutingBehaviour source) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) _source = source; _clearConfirmUntil = 0f; GameObject val = new GameObject("SCR_GroupItemsCanvas", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); val.transform.SetParent(((Component)this).transform, false); Canvas component = val.GetComponent<Canvas>(); component.renderMode = (RenderMode)0; component.overrideSorting = true; component.sortingOrder = SortingOrderAboveInventory(); _canvasTransform = val.transform; _panel = UiFactory.CreatePanel(val.transform, "SCR_GroupItemsPanel", new Vector2(360f, 200f)); _panel.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2; Rebuild(); } private void Close() { if ((Object)(object)_canvasTransform != (Object)null) { Object.Destroy((Object)(object)((Component)_canvasTransform).gameObject); } _canvasTransform = null; _panel = null; _source = null; _lastRegistryBlob = null; _clearConfirmUntil = 0f; } private void Rebuild() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_0187: Expected O, but got Unknown //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Expected O, but got Unknown if ((Object)(object)_panel == (Object)null) { return; } foreach (Transform item in _panel.transform) { ((Component)item).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)item).gameObject); } _lastRegistryBlob = GroupRegistryAccess.CurrentBlob(); ContainerRoutingBehaviour? source = _source; string text = ((source != null) ? source.GetSettings().GroupName : null); GroupRegistry val2 = GroupRegistryAccess.Load(); if (text != null) { GroupDefinition group = val2.GetGroup(text); if (group != null) { List<(string, string)> list = ((IEnumerable<string>)group.ItemTypes).Select((string itemType) => (Type: itemType, Name: ItemTypeResolver.GetDisplayName(itemType))).OrderBy<(string, string), string>(((string Type, string Name) item) => item.Name, StringComparer.CurrentCultureIgnoreCase).ToList(); GameObject obj = UiFactory.CreateRow(_panel.transform, "Header"); ((Component)UiFactory.CreateText(obj.transform, $"{group.Name}: {list.Count} items")).gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f; UiFactory.CreateButton(obj.transform, "Close", new UnityAction(Close)); if (list.Count == 0) { UiFactory.CreateText(_panel.transform, "No items yet. Use Add Contents."); return; } float height = Mathf.Min(360f, (float)list.Count * 36f); RectTransform content = UiFactory.CreateScrollList(_panel.transform, 340f, height).content; foreach (var item2 in list) { BuildItemRow((Transform)(object)content, item2.Item1, item2.Item2); } GameObject obj2 = UiFactory.CreateRow(_panel.transform, "Footer"); UiFactory.CreateButton(label: (_clearConfirmUntil > Time.unscaledTime) ? "Click again to clear all" : "Clear all", parent: obj2.transform, onClick: (UnityAction)delegate { OnClearAllClicked(group.Name); }); return; } } Close(); } private void BuildItemRow(Transform content, string itemType, string displayName) { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) GameObject val = UiFactory.CreateRow(content, "Row_" + itemType); LayoutElement component = val.GetComponent<LayoutElement>(); component.minHeight = 32f; component.preferredHeight = 32f; Sprite icon = ItemTypeResolver.GetIcon(itemType); float minWidth; if (icon != null) { GameObject val2 = new GameObject("Icon", new Type[3] { typeof(RectTransform), typeof(Image), typeof(LayoutElement) }); val2.transform.SetParent(val.transform, false); Image component2 = val2.GetComponent<Image>(); component2.sprite = icon; component2.preserveAspect = true; ((Graphic)component2).raycastTarget = false; LayoutElement component3 = val2.GetComponent<LayoutElement>(); minWidth = (component3.preferredWidth = 28f); component3.minWidth = minWidth; minWidth = (component3.preferredHeight = 28f); component3.minHeight = minWidth; } ((Component)UiFactory.CreateText(val.transform, displayName)).gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f; LayoutElement component4 = ((Component)UiFactory.CreateButton(val.transform, "Remove", (UnityAction)delegate { GroupRegistryRpc.Request(GroupRegistryMutation.UnassignItem(itemType)); })).GetComponent<LayoutElement>(); minWidth = (component4.preferredWidth = 90f); component4.minWidth = minWidth; } private void OnClearAllClicked(string groupName) { if (_clearConfirmUntil <= Time.unscaledTime) { _clearConfirmUntil = Time.unscaledTime + 3f; Rebuild(); return; } _clearConfirmUntil = 0f; Player localPlayer = Player.m_localPlayer; long num = ((localPlayer != null) ? localPlayer.GetPlayerID() : 0); GroupRegistryRpc.Request(GroupRegistryMutation.ReplaceGroupItems(groupName, (IEnumerable<string>)Array.Empty<string>(), num)); Rebuild(); } private static int SortingOrderAboveInventory() { Canvas val = (((Object)(object)InventoryGui.instance != (Object)null) ? ((Component)InventoryGui.instance).GetComponentInParent<Canvas>() : null); if ((Object)(object)val == (Object)null) { return 1; } return ((val.isRootCanvas || val.overrideSorting) ? val : val.rootCanvas).sortingOrder + 1; } } internal sealed class GroupManagementController : MonoBehaviour { private static GroupManagementController? _instance; private const float PanelWidth = 460f; private const float InitialPanelHeight = 300f; private const float MaxContentListHeight = 240f; private GameObject? _panel; private Transform? _canvasTransform; private string? _lastRegistryBlob; private const float RegistryCheckIntervalSeconds = 0.1f; private float _nextRegistryCheck; private bool _textInputBlocked; private bool _nameEditorRequested; private readonly Dictionary<Toggle, string> _evictionToggles = new Dictionary<Toggle, string>(); private ContainerRoutingBehaviour? _source; internal static bool IsNameEditorOpen { get { if ((Object)(object)_instance != (Object)null && _instance._nameEditorRequested && (Object)(object)TextInput.instance != (Object)null && (Object)(object)TextInput.instance.m_panel != (Object)null) { return TextInput.instance.m_panel.activeInHierarchy; } return false; } } private void Awake() { _instance = this; } private void OnDestroy() { if (_instance == this) { _instance = null; } } public static void Toggle(ContainerRoutingBehaviour? source) { if (!((Object)(object)_instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null)) { if ((Object)(object)_instance._panel != (Object)null) { _instance.Close(); } else if ((Object)(object)source != (Object)null) { GroupItemsController.CloseIfOpen(); _instance.Open(source); } } } private void Update() { if ((Object)(object)_panel == (Object)null) { return; } if (!IsSourceChestStillOpen()) { Close(); return; } bool flag = TextInput.IsVisible(); if (_nameEditorRequested && ((Object)(object)TextInput.instance == (Object)null || (Object)(object)TextInput.instance.m_panel == (Object)null || !TextInput.instance.m_panel.activeInHierarchy)) { _nameEditorRequested = false; } SetTextInputBlocked(flag); if ((Object)(object)_panel != (Object)null && _panel.activeSelf == flag) { _panel.SetActive(!flag); } if (!flag && Time.unscaledTime >= _nextRegistryCheck) { _nextRegistryCheck = Time.unscaledTime + 0.1f; string text = GroupRegistryAccess.CurrentBlob(); if (text != null && text != _lastRegistryBlob) { Rebuild(); } } } private bool IsSourceChestStillOpen() { if ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)_source != (Object)null && InventoryGui.IsVisible() && (Object)(object)InventoryGui.instance != (Object)null && InventoryGui.instance.IsContainerOpen()) { return (Object)(object)ContainerPanelController.Current == (Object)(object)_source; } return false; } private static long LocalPlayerId() { Player localPlayer = Player.m_localPlayer; if (localPlayer == null) { return 0L; } return localPlayer.GetPlayerID(); } private void Close() { if ((Object)(object)_canvasTransform != (Object)null) { Object.Destroy((Object)(object)((Component)_canvasTransform).gameObject); } _canvasTransform = null; _panel = null; _source = null; _lastRegistryBlob = null; _nameEditorRequested = false; } private void Open(ContainerRoutingBehaviour source) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) _source = source; GameObject val = new GameObject("SCR_GroupManagementCanvas", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); val.transform.SetParent(((Component)this).transform, false); Canvas component = val.GetComponent<Canvas>(); component.renderMode = (RenderMode)0; component.overrideSorting = true; component.sortingOrder = SortingOrderAboveInventory(); _canvasTransform = val.transform; _panel = UiFactory.CreatePanel(val.transform, "SCR_GroupManagementPanel", new Vector2(460f, 300f)); _panel.AddComponent<ContentSizeFitter>().verticalFit = (FitMode)2; Rebuild(); } private static int SortingOrderAboveInventory() { Canvas val = (((Object)(object)InventoryGui.instance != (Object)null) ? ((Component)InventoryGui.instance).GetComponentInParent<Canvas>() : null); if ((Object)(object)val == (Object)null) { return 1; } return ((val.isRootCanvas || val.overrideSorting) ? val : val.rootCanvas).sortingOrder + 1; } private void Rebuild(string? validationMessage = null) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Expected O, but got Unknown if ((Object)(object)_panel == (Object)null) { return; } _evictionToggles.Clear(); foreach (Transform item in _panel.transform) { ((Component)item).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)item).gameObject); } GameObject obj = UiFactory.CreateRow(_panel.transform, "Header"); ((Component)UiFactory.CreateText(obj.transform, "Storage Groups")).gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f; UiFactory.CreateButton(obj.transform, "Close", new UnityAction(Close)); Toggle obj2 = UiFactory.CreateToggle(UiFactory.CreateRow(_panel.transform, "Options").transform, "Show routing messages", RoutingNotifier.Enabled, delegate(bool value) { RoutingNotifier.Enabled = value; }); LayoutElement component = ((Component)obj2).GetComponent<LayoutElement>(); component.minWidth = 260f; component.preferredWidth = 260f; TMP_Text componentInChildren = ((Component)obj2).GetComponentInChildren<TMP_Text>(); if (componentInChildren != null) { componentInChildren.fontSizeMin = 14f; componentInChildren.fontSizeMax = 18f; componentInChildren.fontSize = 18f; } if (validationMessage != null) { TMP_Text obj3 = UiFactory.CreateText(_panel.transform, validationMessage); ((Graphic)obj3).color = new Color(1f, 0.35f, 0.25f); obj3.textWrappingMode = (TextWrappingModes)1; ((Component)obj3).gameObject.AddComponent<LayoutElement>().minHeight = 44f; } _lastRegistryBlob = GroupRegistryAccess.CurrentBlob(); GroupRegistry val2 = GroupRegistryAccess.Load(); float height = Mathf.Min(240f, (float)(val2.Groups.Count + 1) * 28f); RectTransform content = UiFactory.CreateScrollList(_panel.transform, 440f, height).content; foreach (GroupDefinition item2 in val2.Groups.OrderBy<GroupDefinition, string>((GroupDefinition group) => group.Name, StringComparer.Ordinal)) { BuildGroupRow((Transform)(object)content, val2, item2); } UiFactory.CreateButton(UiFactory.CreateRow((Transform)(object)content, "Row_NewGroup").transform, "+", new UnityAction(OnNewGroupClicked)); } private void BuildGroupRow(Transform content, GroupRegistry registry, GroupDefinition group) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Expected O, but got Unknown //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Expected O, but got Unknown GameObject obj = UiFactory.CreateRow(content, "Row_" + group.Name); ContainerRoutingBehaviour? source = _source; bool isSelected = ((source != null) ? source.GetSettings().GroupName : null) == GroupNameNormalizer.Normalize(group.Name); Button obj2 = UiFactory.CreateButton(obj.transform, $"{group.Name} ({group.ItemTypes.Count})", (UnityAction)delegate { _source?.SetGroup(isSelected ? null : group.Name); Rebuild(); }); ((Component)obj2).GetComponent<LayoutElement>().flexibleWidth = 1f; UiFactory.CreateSelectionBorder(((Component)obj2).transform).SetActive(isSelected); ((Component)obj2).gameObject.AddComponent<GroupContentsTooltip>().Initialize(() => group.Name, _canvasTransform); Toggle val = UiFactory.CreateToggle(obj.transform, "Strict", group.EvictMismatching, delegate(bool value) { SetGroupEviction(group.Name, value); }); LayoutElement component = ((Component)val).GetComponent<LayoutElement>(); component.minWidth = 100f; component.preferredWidth = 100f; ((Selectable)val).interactable = !_textInputBlocked; _evictionToggles.Add(val, group.Name); ((Component)val).gameObject.AddComponent<GroupEvictionTooltip>().Initialize(_canvasTransform); TMP_Text componentInChildren = ((Component)val).GetComponentInChildren<TMP_Text>(); if (componentInChildren != null) { componentInChildren.fontSizeMin = 14f; componentInChildren.fontSizeMax = 18f; componentInChildren.fontSize = 18f; } UiFactory.CreateButton(obj.transform, "Rename", (UnityAction)delegate { RequestGroupRename(group.Name); }); UiFactory.CreateButton(obj.transform, "X", (UnityAction)delegate { GroupRegistryRpc.Request(GroupRegistryMutation.DeleteGroup(group.Name, LocalPlayerId())); ContainerRegistry.ClearGroup(group.Name); Rebuild(); }); } public static void NewGroupFromContents(ContainerRoutingBehaviour source, string suggestedName, IReadOnlyList<string> itemTypes) { if (!((Object)(object)_instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null)) { if ((Object)(object)_instance._panel != (Object)null) { _instance.Close(); } GroupItemsController.CloseIfOpen(); _instance.Open(source); _instance.RequestGroupCreate(suggestedName, itemTypes); } } public static void CloseIfOpen() { if ((Object)(object)_instance != (Object)null && (Object)(object)_instance._panel != (Object)null) { _instance.Close(); } } private void OnNewGroupClicked() { if (!((Object)(object)TextInput.instance == (Object)null)) { GroupRegistry val = GroupRegistryAccess.Load(); int i; for (i = val.Groups.Count + 1; val.HasGroup($"Group-{i}"); i++) { } string suggestedName = $"Group-{i}"; RequestGroupCreate(suggestedName); } } private void RequestGroupCreate(string suggestedName, IReadOnlyList<string>? itemTypes = null) { if ((Object)(object)TextInput.instance == (Object)null) { return; } DelegateTextReceiver delegateTextReceiver = new DelegateTextReceiver(() => suggestedName, delegate(string name) { string text = DisplayNameSanitizer.Sanitize(name); if (text == null) { Rebuild("Group name cannot be empty."); } else { GroupRegistry val = GroupRegistryAccess.Load(); if (val.HasGroup(text)) { Rebuild("A group with that name already exists."); } else { string text2 = "g:" + Guid.NewGuid().ToString("N"); GroupRegistryRpc.Request(GroupRegistryMutation.CreateGroup(text, LocalPlayerId(), text2)); _source?.SetGroup(text, text2); if (itemTypes != null && itemTypes.Count > 0) { string text3 = GroupItemTransfers.Describe(val, text, (IEnumerable<string>)itemTypes, (Func<string, string>)ItemTypeResolver.GetDisplayName); foreach (string itemType in itemTypes) { GroupRegistryRpc.Request(GroupRegistryMutation.AssignItem(itemType, text)); } if (text3 != null) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, text3, 0, (Sprite)null, false); } } } Rebuild(); } } }); TextInput.instance.RequestText((TextReceiver)(object)delegateTextReceiver, "$scr_group_name", 32); _nameEditorRequested = true; PrepareForTextInput(); ((MonoBehaviour)this).StartCoroutine(FocusNameOnNextFrame(selectAll: true)); } private void SetGroupEviction(string groupName, bool value) { if (!GroupRegistryAccess.TryLoad(out GroupRegistry registry)) { Rebuild("Group settings aren't available yet. Try again in a moment."); return; } try { registry.WithEvictMismatching(groupName, value); } catch (ArgumentException) { Rebuild("This group no longer exists."); return; } GroupRegistryRpc.Request(GroupRegistryMutation.SetGroupEviction(groupName, value, LocalPlayerId())); } private void SetTextInputBlocked(bool blocked) { if (_textInputBlocked == blocked) { return; } _textInputBlocked = blocked; if ((Object)(object)_panel == (Object)null) { return; } foreach (Toggle key in _evictionToggles.Keys) { ((Selectable)key).interactable = !blocked; } } private void RequestGroupRename(string oldName) { if ((Object)(object)TextInput.instance == (Object)null) { return; } DelegateTextReceiver delegateTextReceiver = new DelegateTextReceiver(() => oldName, delegate(string newName) { if (string.IsNullOrWhiteSpace(newName) || string.Equals(newName.Trim(), oldName, StringComparison.Ordinal)) { Rebuild(); } else { try { GroupRegistry val = GroupRegistryAccess.Load(); if (val.HasGroup(newName) && GroupNameNormalizer.Normalize(newName) != GroupNameNormalizer.Normalize(oldName)) { throw new ArgumentException("Duplicate group name."); } if (val.HasGroup(oldName)) { val.WithRenamedGroup(oldName, newName); } else if (DisplayNameSanitizer.Sanitize(newName) == null) { throw new ArgumentException("Empty group name."); } } catch (ArgumentException) { Rebuild("A group with that name already exists, or the name is empty."); return; } GroupRegistryRpc.Request(GroupRegistryMutation.RenameGroup(oldName, newName, LocalPlayerId())); Rebuild(); } }); TextInput.instance.RequestText((TextReceiver)(object)delegateTextReceiver, "$scr_group_name", 32); _nameEditorRequested = true; PrepareForTextInput(); ((MonoBehaviour)this).StartCoroutine(FocusNameOnNextFrame(selectAll: false)); } private static IEnumerator FocusNameOnNextFrame(bool selectAll) { yield return null; if ((Object)(object)TextInput.instance == (Object)null || !TextInput.IsVisible()) { yield break; } TMP_InputField componentInChildren = ((Component)TextInput.instance).GetComponentInChildren<TMP_InputField>(true); if (componentInChildren != null) { ((Selectable)componentInChildren).Select(); componentInChildren.ActivateInputField(); if (selectAll) { componentInChildren.selectionAnchorPosition = 0; componentInChildren.selectionFocusPosition = componentInChildren.text.Length; } else { componentInChildren.caretPosition = componentInChildren.text.Length; } yield break; } InputField componentInChildren2 = ((Component)TextInput.instance).GetComponentInChildren<InputField>(true); if (componentInChildren2 != null) { ((Selectable)componentInChildren2).Select(); componentInChildren2.ActivateInputField(); if (selectAll) { componentInChildren2.selectionAnchorPosition = 0; componentInChildren2.selectionFocusPosition = componentInChildren2.text.Length; } else { componentInChildren2.caretPosition = componentInChildren2.text.Length; } } } private void PrepareForTextInput() { SetTextInputBlocked(blocked: true); GameObject? panel = _panel; if (panel != null) { panel.SetActive(false); } } } public static class GroupRegistryAccess { private static readonly GroupRegistryCache Cache = new GroupRegistryCache((Func<string, GroupRegistry>)DecodeAndBackUpIfCorrupt); public static Func<ZDO?> AnchorZdoProvider { get; set; } = () => (ZDO?)null; public static GroupRegistry Load() { TryLoad(out GroupRegistry registry); return registry; } public static bool TryLoad(out GroupRegistry registry) { return Cache.TryGet(CurrentBlob(), ref registry); } public static string? CurrentBlob() { ZDO obj = AnchorZdoProvider(); if (obj == null) { return null; } return obj.GetString("SCR_Groups", string.Empty); } private static GroupRegistry DecodeAndBackUpIfCorrupt(string? blob) { GroupRegistry result = default(GroupRegistry); if (GroupRegistryCodec.TryDeserialize(blob, ref result)) { return result; } ZDO val = AnchorZdoProvider(); if (val != null) { val.Set("SCR_GroupsCorruptBackup", blob ?? string.Empty); } return result; } public static string? DisplayName(string? groupKey) { object obj2; if (groupKey != null) { GroupDefinition obj = Load().GetGroup(groupKey); obj2 = ((obj != null) ? obj.Name : null); if (obj2 == null) { return groupKey; } } else { obj2 = null; } return (string?)obj2; } public static bool IsEvicting(string groupKey) { GroupDefinition obj = Load().GetGroup(groupKey); if (obj == null) { return false; } return obj.EvictMismatching; } } internal sealed class HoverTooltip : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler { private const float Width = 260f; private const float TextCheckIntervalSeconds = 0.25f; private Func<string?>? _text; private GameObject? _panel; private bool _hovered; private string? _shownText; private float _nextTextCheck; public void Initialize(Func<string?> text) { _text = text; } public void OnPointerEnter(PointerEventData eventData) { _hovered = true; Show(_text?.Invoke()); } public void OnPointerExit(PointerEventData eventData) { _hovered = false; Hide(); } private void OnDisable() { _hovered = false; Hide(); } private void Update() { if (_hovered && !(Time.unscaledTime < _nextTextCheck)) { _nextTextCheck = Time.unscaledTime + 0.25f; string text = _text?.Invoke(); if (text != _shownText) { Show(text); } } } private void Show(string? body) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) Hide(); _shownText = body; if (body != null && body.Length > 0) { int num = body.Split(new char[1] { '\n' }).Length; float num2 = 20f + (float)num * 19f; _panel = UiFactory.CreatePanel(((Component)this).transform, "SCR_HoverTooltip", new Vector2(260f, num2)); RectTransform component = _panel.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(0f, 1f); component.pivot = new Vector2(1f, 1f); component.anchoredPosition = new Vector2(-8f, 0f); ((Graphic)_panel.GetComponent<Image>()).raycastTarget = false; TMP_Text obj = UiFactory.CreateText(_panel.transform, body); obj.alignment = (TextAlignmentOptions)257; obj.fontSize = 14f; obj.textWrappingMode = (TextWrappingModes)0; obj.overflowMode = (TextOverflowModes)1; ((Graphic)obj).raycastTarget = false; ((Component)obj).gameObject.AddComponent<LayoutElement>().preferredHeight = num2 - 20f; } } private void Hide() { if ((Object)(object)_panel != (Object)null) { _panel.SetActive(false); Object.Destroy((Object)(object)_panel); _panel = null; } } } [HarmonyPatch(typeof(InventoryGui), "Awake")] internal static class InventoryGuiAwakePatch { private static void Postfix(InventoryGui __instance) { UiFactory.CaptureVanillaStyle(__instance); } } [HarmonyPatch(typeof(InventoryGui), "Show", new Type[] { typeof(Container), typeof(int) })] internal static class InventoryGuiContainerShowPatch { private static void Postfix(InventoryGui __instance, Container container) { if (!((Object)(object)container == (Object)null)) { ContainerPanelController.ShowFor(__instance, container); } } } [HarmonyPatch(typeof(InventoryGui), "Update")] internal static class InventoryGuiNameEditInputPatch { private static void Prefix() { if (GroupManagementController.IsNameEditorOpen) { ZInput.ResetButtonStatus("Use"); ZInput.ResetButtonStatus("JoyUse"); } } } internal static class ItemTypeResolver { public static string GetItemType(ItemData item) { if (!((Object)(object)item.m_dropPrefab != (Object)null)) { return item.m_shared.m_name; } return ((Object)item.m_dropPrefab).name; } public static string GetDisplayName(string itemType) { ObjectDB instance = ObjectDB.instance; object obj; if (instance == null) { obj = null; } else { GameObject itemPrefab = instance.GetItemPrefab(itemType); obj = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name); } string text = (string)obj; if (string.IsNullOrEmpty(text)) { return itemType; } Localization instance2 = Localization.instance; return ((instance2 != null) ? instance2.Localize(text) : null) ?? itemType; } public static Sprite? GetIcon(string itemType) { GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(itemType) : null); Sprite[] array = ((!((Object)(object)val != (Object)null)) ? null : val.GetComponent<ItemDrop>()?.m_itemData?.m_shared)?.m_icons; if (array == null || array.Length <= 0) { return null; } return array[0]; } } [HarmonyPatch(typeof(Localization), "SetupLanguage")] internal static class LocalizationPatch { private static readonly MethodInfo AddWordMethod = AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null); internal static void AddWord(Localization instance, string key, string text) { AddWordMethod.Invoke(instance, new object[2] { key, text }); } private static void Postfix(Localization __instance) { AddWord(__instance, "scr_group_name", "Group name"); } } internal static class UiFactory { private const float MaxControlFontSize = 19.2f; private static bool _captured; private static TMP_FontAsset? _font; private static Material? _fontMaterial; private static Color _textColor = Color.white; private static float _fontSize = 14f; private static Sprite? _buttonSprite; private static Type _buttonSpriteType = (Type)0; private static Transition _buttonTransition = (Transition)1; private static ColorBlock _buttonColors = ColorBlock.defaultColorBlock; private static SpriteState _buttonSpriteState; private static Sprite? _toggleBackgroundSprite; private static Type _toggleBackgroundSpriteType = (Type)0; private static Sprite? _toggleCheckmarkSprite; private static readonly Color PanelColor = new Color(0.07f, 0.06f, 0.05f, 1f); private static readonly Color PanelEdgeColor = new Color(0.45f, 0.35f, 0.18f, 1f); public static void CaptureVanillaStyle(InventoryGui gui) { //IL_004b: 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_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: 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_00dc: Unknown result type (might be due to invalid IL or missing references) if (_captured || (Object)(object)gui == (Object)null) { return; } _captured = true; if ((Object)(object)gui.m_containerName != (Object)null) { _font = gui.m_containerName.font; _fontMaterial = gui.m_containerName.fontSharedMaterial; _textColor = ((Graphic)gui.m_containerName).color; _fontSize = gui.m_containerName.fontSize; } Button[] array = (Button[])(object)new Button[3] { gui.m_takeAllButton, gui.m_stackAllButton, gui.m_dropButton }; foreach (Button val in array) { if (!((Object)(object)val == (Object)null)) { Image component = ((Component)val).GetComponent<Image>(); if (component != null && component.sprite != null) { _buttonSprite = component.sprite; _buttonSpriteType = component.type; _buttonTransition = ((Selectable)val).transition; _buttonColors = ((Selectable)val).colors; _buttonSpriteState = ((Selectable)val).spriteState; break; } } } if ((Object)(object)gui.m_pvp != (Object)null) { Image component2 = ((Component)gui.m_pvp).GetComponent<Image>(); if ((Object)(object)component2 != (Object)null) { _toggleBackgroundSprite = component2.sprite; _toggleBackgroundSpriteType = component2.type; } Graphic graphic = gui.m_pvp.graphic; Image val2 = (Image)(object)((graphic is Image) ? graphic : null); if (val2 != null) { _toggleCheckmarkSprite = val2.sprite; } } } public static GameObject CreatePanel(Transform parent, string name, Vector2 size) { //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_0040: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_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_0096: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_010b: Expected O, but got Unknown GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(Image), typeof(VerticalLayoutGroup) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0.5f, 0.5f); component.anchorMax = new Vector2(0.5f, 0.5f); component.pivot = new Vector2(0.5f, 0.5f); component.anchoredPosition = Vector2.zero; component.sizeDelta = size; ((Graphic)val.GetComponent<Image>()).color = PanelColor; Outline obj = val.AddComponent<Outline>(); ((Shadow)obj).effectColor = PanelEdgeColor; ((Shadow)obj).effectDistance = new Vector2(2f, -2f); VerticalLayoutGroup component2 = val.GetComponent<VerticalLayoutGroup>(); ((LayoutGroup)component2).padding = new RectOffset(10, 10, 10, 10); ((HorizontalOrVerticalLayoutGroup)component2).spacing = 6f; ((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; return val; } public static GameObject CreateSelectionBorder(Transform parent) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //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_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_0158: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown GameObject val = new GameObject("SelectionBorder", new Type[2] { typeof(RectTransform), typeof(LayoutElement) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = new Vector2(1f, 1f); component.offsetMax = new Vector2(-1f, -1f); val.GetComponent<LayoutElement>().ignoreLayout = true; AddBorderBar(val.transform, "Top", new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, 3f)); AddBorderBar(val.transform, "Bottom", Vector2.zero, new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 3f)); AddBorderBar(val.transform, "Left", Vector2.zero, new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(3f, 0f)); AddBorderBar(val.transform, "Right", new Vector2(1f, 0f), Vector2.one, new Vector2(1f, 0.5f), new Vector2(3f, 0f)); val.SetActive(false); return val; } private static void AddBorderBar(Transform parent, string name, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 size) { //IL_0021: 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_0033: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(Image) }); val.transform.SetParent(parent, false); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = anchorMin; component.anchorMax = anchorMax; component.pivot = pivot; component.sizeDelta = size; Image component2 = val.GetComponent<Image>(); ((Graphic)component2).color = new Color(1f, 0.84f, 0.12f, 1f); ((Graphic)component2).raycastTarget = false; } public static GameObject CreateRow(Transform parent, string name) { //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_0040: 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_008f: Expected O, but got Unknown GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(HorizontalLayoutGroup), typeof(LayoutElement) }); val.transform.SetParent(parent, false); HorizontalLayoutGroup component = val.GetComponent<HorizontalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)component).spacing = 6f; ((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; ((LayoutGroup)component).childAlignment = (TextAnchor)3; LayoutElement component2 = val.GetComponent<LayoutElement>(); component2.minHeight = 24f; component2.preferredHeight = 24f; return val; } public static ScrollRect CreateScrollList(Transform parent, float width, float height) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_004d: 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_00a2: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0163: 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_017a: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Expected O, but got Unknown //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_0366: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ScrollList", new Type[3] { typeof(RectTransform), typeof(ScrollRect), typeof(LayoutElement) }); val.transform.SetParent(parent, false); val.GetComponent<RectTransform>().sizeDelta = new Vector2(width, height); LayoutElement component = val.GetComponent<LayoutElement>(); component.preferredHeight = height; component.minHeight = height; GameObject val2 = new GameObject("Viewport", new Type[3] { typeof(RectTransform), typeof(RectMask2D), typeof(Image) }); val2.transform.SetParent(val.transform, false); RectTransform component2 = val2.GetComponent<RectTransform>(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = Vector2.zero; component2.offsetMax = new Vector2(-14f, 0f); component2.pivot = new Vector2(0f, 1f); Image component3 = val2.GetComponent<Image>(); ((Graphic)component3).color = new Color(1f, 1f, 1f, 0f); ((Graphic)component3).raycastTarget = true; GameObject val3 = new GameObject("Content", new Type[3] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter) }); val3.transform.SetParent(val2.transform, false); RectTransform component4 = val3.GetComponent<RectTransform>(); component4.anchorMin = new Vector2(0f, 1f); component4.anchorMax = new Vector2(1f, 1f); component4.pivot = new Vector2(0f, 1f); component4.anchoredPosition = Vector2.zero; component4.sizeDelta = Vector2.zero; VerticalLayoutGroup component5 = val3.GetComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)component5).spacing = 4f; ((HorizontalOrVerticalLayoutGroup)component5).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component5).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component5).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)component5).childForceExpandHeight = false; val3.GetComponent<ContentSizeFitter>().verticalFit = (FitMode)2; ScrollRect component6 = val.GetComponent<ScrollRect>(); component6.horizontal = false; component6.vertical = true; component6.scrollSensitivity = 64f; component6.movementType = (MovementType)2; component6.viewport = component2; component6.content = component4; GameObject val4 = new GameObject("Scrollbar", new Type[3] { typeof(RectTransform), typeof(Image), typeof(Scrollbar) }); val4.transform.SetParent(val.transform, false); RectTransform component7 = val4.GetComponent<RectTransform>(); component7.anchorMin = new Vector2(1f, 0f); component7.anchorMax = Vector2.one; component7.pivot = new Vector2(1f, 0.5f); component7.sizeDelta = new Vector2(10f, 0f); component7.anchoredPosition = new Vector2(-1f, 0f); ((Graphic)val4.GetComponent<Image>()).color = new Color(0.18f, 0.13f, 0.08f, 0.85f); GameObject val5 = new GameObject("Handle", new Type[2] { typeof(RectTransform), typeof(Image) }); val5.transform.SetParent(val4.transform, false); RectTransform component8 = val5.GetComponent<RectTransform>(); component8.anchorMin = Vector2.zero; component8.anchorMax = Vector2.one; component8.offsetMin = Vector2.zero; component8.offsetMax = Vector2.zero; Image component9 = val5.GetComponent<Image>(); ((Graphic)component9).color = new Color(1f, 0.84f, 0.12f, 1f); Scrollbar component10 = val4.GetComponent<Scrollbar>(); component10.direction = (Direction)2; component10.handleRect = component8; ((Selectable)component10).targetGraphic = (Graphic)(object)component9; component6.verticalScrollbar = component10; component6.verticalScrollbarVisibility = (ScrollbarVisibility)1; return component6; } public static TMP_Text CreateText(Transform parent, string initialText) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) GameObject val = new GameObject("Text", new Type[1] { typeof(RectTransform) }); val.SetActive(false); val.transform.SetParent(parent, false); TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val2).text = initialText; ((Graphic)val2).color = _textColor; ((TMP_Text)val2).fontSize = Mathf.Min(_fontSize, 19.2f); ((TMP_Text)val2).alignment = (TextAlignmentOptions)4097; ((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0; if ((Object)(object)_font != (Object)null) { ((TMP_Text)val2).font = _font; } if ((Object)(object)_fontMaterial != (Object)null) { ((TMP_Text)val2).fontSharedMaterial = _fontMaterial; } val.SetActive(true); return (TMP_Text)(object)val2; } public static Button CreateButton(Transform parent, string label, UnityAction onClick) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009d: 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_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Button_" + label, new Type[3] { typeof(RectTransform), typeof(Image), typeof(Button) }); val.transform.SetParent(parent, false); Image component = val.GetComponent<Image>(); Button component2 = val.GetComponent<Button>(); if ((Object)(object)_buttonSprite != (Object)null) { component.sprite = _buttonSprite; component.type = _buttonSpriteType; ((Graphic)component).color = Color.white; ((Selectable)component2).transition = _buttonTransition; ((Selectable)component2).colors = _buttonColors; ((Selectable)component2).spriteState = _buttonSpriteState; } else { ((Graphic)component).color = new Color(0.2f, 0.2f, 0.2f, 1f); } ((UnityEvent)component2.onClick).AddListener(onClick); TMP_Text obj = CreateText(val.transform, label); obj.alignment = (TextAlignmentOptions)4098; ApplyControlFontSize(obj); RectTransform component3 = ((Component)obj).GetComponent<RectTransform>(); component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.offsetMin = Vector2.zero; component3.offsetMax = Vector2.zero; LayoutElement obj2 = val.AddComponent<LayoutElement>(); obj2.minHeight = 22f; obj2.minWidth = 56f; return component2; } private static void ApplyControlFontSize(TMP_Text text) { float fontSizeMax = (text.fontSize = Mathf.Min(_fontSize, 19.2f)); text.enableAutoSizing = true; text.fontSizeMin = 8f; text.fontSizeMax = fontSizeMax; text.overflowMode = (TextOverflowModes)1; } public static Toggle CreateToggle(Transform parent, string label, bool initialValue, UnityAction<bool> onValueChanged) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_0128: Expected O, but got Unknown //IL_0154: 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_017e: Unknown result type (might be d