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 Crucible v0.3.34
plugins/Crucible/Crucible.Contracts.dll
Decompiled 10 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExtraPotions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")] [assembly: AssemblyFileVersion("0.2.0.0")] [assembly: AssemblyInformationalVersion("0.3.34+fatal: not a git repository (or any of the parent directories): .git")] [assembly: AssemblyProduct("Crucible.Contracts")] [assembly: AssemblyTitle("Crucible.Contracts")] [assembly: AssemblyVersion("0.2.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 ExtraPotions.Meadworks.Contracts { public sealed class AsyncResourceContext { public string ActorId { get; } public long WorldId { get; } public string SessionId { get; } public string RequestId { get; } public AsyncResourceContext(string actorId, long worldId, string sessionId, string requestId) { Text(actorId); Text(sessionId); Text(requestId); if (worldId == 0L) { throw new ArgumentException("Missing world."); } ActorId = actorId; WorldId = worldId; SessionId = sessionId; RequestId = requestId; } private static void Text(string value) { if (string.IsNullOrWhiteSpace(value) || value.Length > 128 || value.IndexOfAny(new char[3] { '\0', '\r', '\n' }) >= 0) { throw new ArgumentException("Invalid resource correlation identity."); } } } public enum AsyncReservationState { Pending, Reserved, CommitPending, Committed, RolledBack, Failed, RecoveryRequired } public sealed class ResourceReservationResult { public AsyncReservationState State { get; } public IAsyncResourceReservation? Reservation { get; } public ResourceReservationResult(AsyncReservationState state, IAsyncResourceReservation? reservation) { if (!Enum.IsDefined(typeof(AsyncReservationState), state)) { throw new ArgumentException("Unknown resource state."); } if (reservation == null && (state == AsyncReservationState.Reserved || state == AsyncReservationState.CommitPending || state == AsyncReservationState.Committed || state == AsyncReservationState.RecoveryRequired)) { throw new ArgumentException("A hold or decision requires its reservation identity."); } State = state; Reservation = reservation; } } public interface IAsyncResourceProvider { Task<int> GetAvailableAsync(AsyncResourceContext context, string itemId, CancellationToken cancellation); Task<ResourceReservationResult> TryReserveAsync(AsyncResourceContext context, IReadOnlyList<ResourceCost> costs, CancellationToken cancellation); } public interface IAsyncResourceReservation : IDisposable { AsyncResourceContext Identity { get; } AsyncReservationState State { get; } Task<AsyncReservationState> CommitAsync(CancellationToken cancellation); Task<AsyncReservationState> RollbackAsync(CancellationToken cancellation); Task<AsyncReservationState> GetStatusAsync(CancellationToken cancellation); } public static class EmberSleepContract { public const string Id = "meadworks.ember.sleep"; public static readonly Version ContractVersion = new Version(1, 0); public const int MaximumPlayers = 1024; public const double MaximumExtraRate = 30.0; public static Type DelegateType => typeof(Func<Tuple<bool, int, int, int, double, bool, float>>); public static Tuple<bool, int, int, int, double, bool, float> Pack(EmberSleepState state) { return Tuple.Create(state.WindowOpen, state.Players, state.Sleeping, state.Required, state.ExtraRate, state.SkipRunning, state.SkipProgress); } public static bool TryRead(Tuple<bool, int, int, int, double, bool, float>? raw, out EmberSleepState state) { state = EmberSleepState.Closed; if (raw == null || !EmberSleepState.Valid(raw.Item2, raw.Item3, raw.Item4, raw.Item5, raw.Item7)) { return false; } state = new EmberSleepState(raw.Item1, raw.Item2, raw.Item3, raw.Item4, raw.Item5, raw.Item6, raw.Item7); return true; } public static bool TryRead(Func<Tuple<bool, int, int, int, double, bool, float>>? provider, out EmberSleepState state) { state = EmberSleepState.Closed; if (provider == null) { return false; } Tuple<bool, int, int, int, double, bool, float> raw; try { raw = provider(); } catch { return false; } return TryRead(raw, out state); } } public readonly struct EmberSleepState : IEquatable<EmberSleepState> { public static EmberSleepState Closed => default(EmberSleepState); public bool WindowOpen { get; } public int Players { get; } public int Sleeping { get; } public int Required { get; } public double ExtraRate { get; } public bool SkipRunning { get; } public float SkipProgress { get; } public bool Boosting => ExtraRate > 0.0; public EmberSleepState(bool windowOpen, int players, int sleeping, int required, double extraRate, bool skipRunning, float skipProgress) { if (players < 0 || players > 1024) { throw new ArgumentOutOfRangeException("players"); } if (sleeping < 0 || sleeping > players) { throw new ArgumentOutOfRangeException("sleeping"); } if (required < 0 || required > players) { throw new ArgumentOutOfRangeException("required"); } if (double.IsNaN(extraRate) || double.IsInfinity(extraRate) || extraRate < 0.0 || extraRate > 30.0) { throw new ArgumentOutOfRangeException("extraRate"); } if (float.IsNaN(skipProgress) || float.IsInfinity(skipProgress) || skipProgress < 0f || skipProgress > 1f) { throw new ArgumentOutOfRangeException("skipProgress"); } WindowOpen = windowOpen; Players = players; Sleeping = sleeping; Required = required; ExtraRate = extraRate; SkipRunning = skipRunning; SkipProgress = skipProgress; } internal static bool Valid(int players, int sleeping, int required, double extraRate, float skipProgress) { if (players >= 0 && players <= 1024 && sleeping >= 0 && sleeping <= players && required >= 0 && required <= players && !double.IsNaN(extraRate) && !double.IsInfinity(extraRate) && extraRate >= 0.0 && extraRate <= 30.0 && !float.IsNaN(skipProgress) && !float.IsInfinity(skipProgress) && skipProgress >= 0f) { return skipProgress <= 1f; } return false; } public bool Equals(EmberSleepState other) { if (WindowOpen == other.WindowOpen && Players == other.Players && Sleeping == other.Sleeping && Required == other.Required && ExtraRate.Equals(other.ExtraRate) && SkipRunning == other.SkipRunning) { return SkipProgress.Equals(other.SkipProgress); } return false; } public override bool Equals(object? obj) { if (obj is EmberSleepState other) { return Equals(other); } return false; } public override int GetHashCode() { return ((((((((WindowOpen ? 1 : 0) * 397) ^ Players) * 397) ^ Sleeping) * 397) ^ Required) * 397) ^ ExtraRate.GetHashCode() ^ (SkipRunning ? 7919 : 0) ^ SkipProgress.GetHashCode(); } public override string ToString() { return (WindowOpen ? "open" : "closed") + " " + Sleeping + "/" + Players + " (need " + Required + ") rate " + ExtraRate.ToString(CultureInfo.InvariantCulture) + (SkipRunning ? (" skip " + SkipProgress.ToString("0.00", CultureInfo.InvariantCulture)) : ""); } } public static class ExploreRadiusContract { public const string Id = "meadworks.explore.radius"; public static readonly Version ContractVersion = new Version(1, 0); public const float NeutralFactor = 1f; public const float MaximumFactor = 16f; public static Type DelegateType => typeof(Func<float>); public static bool Valid(float factor) { if (!float.IsNaN(factor) && !float.IsInfinity(factor) && factor > 0f) { return factor <= 16f; } return false; } public static bool TryRead(Func<float>? provider, out float factor) { factor = 1f; if (provider == null) { return false; } float num; try { num = provider(); } catch { return false; } if (!Valid(num)) { return false; } factor = num; return true; } public static float ReadFactor(Func<float>? provider) { if (!TryRead(provider, out var factor)) { return 1f; } return factor; } } public static class ExploreCurrentContract { public const string Id = "meadworks.explore.current"; public static readonly Version ContractVersion = new Version(1, 0); public const float MaximumRadius = 10000f; public static Type DelegateType => typeof(Func<float>); public static bool Valid(float radius) { if (!float.IsNaN(radius) && !float.IsInfinity(radius) && radius >= 0f) { return radius <= 10000f; } return false; } public static bool TryRead(Func<float>? provider, out float radius) { radius = 0f; if (provider == null) { return false; } float num; try { num = provider(); } catch { return false; } if (!Valid(num)) { return false; } radius = num; return true; } } public static class FeatureOwnerContract { public const string Prefix = "meadworks.owns."; public static readonly Version ContractVersion = new Version(1, 0); public const string Inventory = "inventory"; public const string StowRow = "stow_row"; public const string Stacks = "stacks"; public const string ExploreRadius = "explore_radius"; public const string EliteCreatures = "elite_creatures"; public const string DebugBuild = "debug_build"; public static Type DelegateType => typeof(Func<bool>); public static string Id(string feature) { if (!ValidFeature(feature)) { throw new ArgumentException("Expected a lowercase feature name.", "feature"); } return "meadworks.owns." + feature; } public static bool ValidFeature(string? feature) { if (string.IsNullOrEmpty(feature) || feature.Length > 64 || feature[0] < 'a' || feature[0] > 'z') { return false; } foreach (char c in feature) { if ((c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '_') { return false; } } return true; } public static bool TryRead(Func<bool>? provider, out bool on) { on = false; if (provider == null) { return false; } try { on = provider(); return true; } catch { on = false; return false; } } } public static class FavouritesContract { public const string Id = "meadworks.items.favorites"; public static readonly Version ContractVersion = new Version(1, 0); public static Type DelegateType => typeof(Func<IReadOnlyCollection<string>>); public static bool TryRead(Func<IReadOnlyCollection<string>>? provider, out IReadOnlyCollection<string> names) { names = (IReadOnlyCollection<string>)(object)Array.Empty<string>(); if (provider == null) { return false; } try { IReadOnlyCollection<string> readOnlyCollection = provider(); if (readOnlyCollection == null) { return false; } names = readOnlyCollection; return true; } catch { return false; } } public static bool Contains(IReadOnlyCollection<string> names, string? prefabName, string? sharedName) { if (names == null) { return false; } foreach (string name in names) { if (name != null && (name == prefabName || name == sharedName)) { return true; } } return false; } } public static class InventoryLayoutContract { public const string Id = "meadworks.inventory.layout"; public static readonly Version ContractVersion = new Version(1, 0); public const int MaximumWidth = 64; public const int MaximumRows = 128; public static Type DelegateType => typeof(Func<Tuple<bool, int, int, int>>); public static Tuple<bool, int, int, int> Pack(InventoryLayout layout) { return Tuple.Create(layout.LaysOut, layout.Width, layout.Rows, layout.Blocked); } public static bool Valid(bool laysOut, int width, int rows, int blocked) { if (laysOut) { if (width >= 1 && width <= 64 && rows >= 0 && rows <= 128 && blocked >= 0 && blocked < width) { if (rows <= 0) { return blocked == 0; } return true; } return false; } return true; } public static bool TryRead(Func<Tuple<bool, int, int, int>>? provider, out InventoryLayout layout) { layout = InventoryLayout.None; if (provider == null) { return false; } Tuple<bool, int, int, int> tuple; try { tuple = provider(); } catch { return false; } if (tuple == null || !Valid(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4)) { return false; } layout = (tuple.Item1 ? new InventoryLayout(laysOut: true, tuple.Item2, tuple.Item3, tuple.Item4) : InventoryLayout.None); return true; } } public readonly struct InventoryLayout : IEquatable<InventoryLayout> { public static InventoryLayout None => default(InventoryLayout); public bool LaysOut { get; } public int Width { get; } public int Rows { get; } public int Blocked { get; } public InventoryLayout(bool laysOut, int width, int rows, int blocked) { if (!InventoryLayoutContract.Valid(laysOut, width, rows, blocked)) { throw new ArgumentOutOfRangeException("width"); } LaysOut = laysOut; Width = (laysOut ? width : 0); Rows = (laysOut ? rows : 0); Blocked = (laysOut ? blocked : 0); } public bool InMain(int x, int y) { if (LaysOut && x >= 0 && y >= 0 && x < Width && y < Rows) { if (y == Rows - 1) { return x < Width - Blocked; } return true; } return false; } public bool Equals(InventoryLayout other) { if (LaysOut == other.LaysOut && Width == other.Width && Rows == other.Rows) { return Blocked == other.Blocked; } return false; } public override bool Equals(object? obj) { if (obj is InventoryLayout other) { return Equals(other); } return false; } public override int GetHashCode() { return ((((((LaysOut ? 1 : 0) * 397) ^ Width) * 397) ^ Rows) * 397) ^ Blocked; } } public static class KeyRingContract { public const string Id = "meadworks.inventory.keyring"; public static readonly Version ContractVersion = new Version(1, 0); public const int MaximumStack = 100000; public static Type DelegateType => typeof(Func<Tuple<int, IReadOnlyCollection<string>>>); public static bool TryRead(Func<Tuple<int, IReadOnlyCollection<string>>>? provider, out int stack, out IReadOnlyCollection<string> items) { stack = 0; items = (IReadOnlyCollection<string>)(object)Array.Empty<string>(); if (provider == null) { return false; } Tuple<int, IReadOnlyCollection<string>> tuple; try { tuple = provider(); } catch { return false; } if (tuple == null || tuple.Item2 == null || tuple.Item1 < 0 || tuple.Item1 > 100000) { return false; } stack = tuple.Item1; items = tuple.Item2; return true; } } public static class HudOccupiedContract { public const string Id = "meadworks.hud.occupied"; public static readonly Version ContractVersion = new Version(1, 0); public const float MaximumExtent = 100000f; public static Type DelegateType => typeof(Func<Tuple<bool, float, float, float, float>>); private static bool Finite(float v) { if (!float.IsNaN(v) && !float.IsInfinity(v) && v >= -100000f) { return v <= 100000f; } return false; } public static bool TryRead(Func<Tuple<bool, float, float, float, float>>? provider, out bool shown, out float left, out float top, out float width, out float height) { shown = false; left = (top = (width = (height = 0f))); if (provider == null) { return false; } Tuple<bool, float, float, float, float> tuple; try { tuple = provider(); } catch { return false; } if (tuple == null) { return false; } if (!tuple.Item1) { return true; } if (!Finite(tuple.Item2) || !Finite(tuple.Item3) || !Finite(tuple.Item4) || !Finite(tuple.Item5) || tuple.Item4 <= 0f || tuple.Item5 <= 0f) { return false; } shown = true; left = tuple.Item2; top = tuple.Item3; width = tuple.Item4; height = tuple.Item5; return true; } } public static class HudColumnSquareContract { public const string Id = "meadworks.hud.column_square"; public static readonly Version ContractVersion = new Version(1, 0); public static Type DelegateType => typeof(Func<object, Tuple<object, object, object, object>>); public static bool TryMake(Func<object, Tuple<object, object, object, object>>? provider, object column, out Tuple<object, object, object, object>? square) { square = null; if (provider == null || column == null) { return false; } try { square = provider(column); } catch { square = null; return false; } if (square == null || square.Item1 == null || square.Item2 == null || square.Item3 == null || square.Item4 == null) { square = null; return false; } return true; } } public static class CraftingApiContract { public const string Id = "meadworks.crafting.api"; public static readonly Version ContractVersion = new Version(1, 0); public const string ApiVersionKey = "GetApiVersion"; public static Type DelegateType => typeof(IReadOnlyDictionary<string, Delegate>); public static bool TryGet<T>(IReadOnlyDictionary<string, Delegate>? api, string name, out T? endpoint) where T : Delegate { endpoint = null; if (api == null || name == null || !api.TryGetValue(name, out Delegate value)) { return false; } endpoint = value as T; return endpoint != null; } public static int ApiVersion(IReadOnlyDictionary<string, Delegate>? api) { if (!TryGet<Func<int>>(api, "GetApiVersion", out Func<int> endpoint)) { return 0; } try { return Math.Max(0, endpoint()); } catch { return 0; } } } public static class SalvageRuneContract { public const string Id = "meadworks.crafting.salvage_rune"; public static readonly Version ContractVersion = new Version(1, 0); public static Type DelegateType => typeof(Tuple<Func<object, string>, Func<object, float>>); } public static class ItemDecorationContract { public const string Id = "meadworks.items.decoration"; public static readonly Version ContractVersion = new Version(1, 0); public static Type DelegateType => typeof(Tuple<Func<object, string>, Func<object, string>, Func<object, Tuple<string, string, float, string, string, string>[]>>); } public static class WearExemptContract { public const string Id = "meadworks.items.wear_exempt"; public static readonly Version ContractVersion = new Version(1, 0); public static Type DelegateType => typeof(Func<object, object, bool>); public static bool Exempt(Func<object, object, bool>? provider, object character, object item) { if (provider == null || character == null || item == null) { return false; } try { return provider(character, item); } catch { return false; } } } public static class AdminCommandContract { public const string Id = "meadworks.admin.command"; public static readonly Version ContractVersion = new Version(1, 0); public static Type DelegateType => typeof(Func<string[], bool>); public static bool TryRun(Func<string[], bool>? provider, params string[] args) { if (provider == null || args == null || args.Length == 0) { return false; } for (int i = 0; i < args.Length; i++) { if (args[i] == null) { return false; } } try { return provider((string[])args.Clone()); } catch { return false; } } } public static class DelveAdminContract { public const string Id = "meadworks.delve.admin"; public static readonly Version ContractVersion = new Version(1, 0); public const string Status = "status"; public const string Actions = "actions"; public const string DryRun = "dry_run"; public const string Request = "request"; public const string Sites = "sites"; public const string Nature = "nature"; public const string Nearest = "nearest"; public const string SurfaceNature = "surface_nature"; public const string SurfaceResources = "surface_resources"; public const int MaximumLines = 4096; public static Type DelegateType => typeof(Func<string, IReadOnlyList<string>, Tuple<bool, IReadOnlyList<string>>>); public static bool TryRun(Func<string, IReadOnlyList<string>, Tuple<bool, IReadOnlyList<string>>>? provider, string operation, IReadOnlyList<string> arguments, out IReadOnlyList<string> lines) { lines = Array.Empty<string>(); if (provider == null || string.IsNullOrEmpty(operation) || arguments == null) { return false; } Tuple<bool, IReadOnlyList<string>> tuple; try { tuple = provider(operation, arguments); } catch { return false; } if (tuple == null || !tuple.Item1 || tuple.Item2 == null || tuple.Item2.Count > 4096) { return false; } foreach (string item in tuple.Item2) { if (item == null) { return false; } } lines = tuple.Item2; return true; } } public sealed class ResourceContext { public string ActorId { get; } public string WorldId { get; } public string RequestId { get; } public ResourceContext(string actorId, string worldId, string requestId) { if (string.IsNullOrWhiteSpace(actorId) || string.IsNullOrWhiteSpace(worldId) || string.IsNullOrWhiteSpace(requestId)) { throw new ArgumentException("Resource context requires actor, world and request identity"); } ActorId = actorId; WorldId = worldId; RequestId = requestId; } } public sealed class ResourceCost { public string ItemId { get; } public int Amount { get; } public ResourceCost(string itemId, int amount) { if (string.IsNullOrWhiteSpace(itemId) || amount <= 0) { throw new ArgumentException("A resource cost requires an item and positive amount"); } ItemId = itemId; Amount = amount; } } public enum ReservationState { Reserved, Committed, RolledBack } public interface IResourceReservation : IDisposable { ReservationState State { get; } void Commit(); void Rollback(); } public interface IResourceProvider { int GetAvailable(ResourceContext context, string itemId); bool TryReserve(ResourceContext context, IReadOnlyList<ResourceCost> costs, out IResourceReservation? reservation); } public enum SeasonKind { Spring, Summer, Fall, Winter } public static class SeasonsContract { public const string Id = "meadworks.seasons.view"; public static readonly Version ContractVersion = new Version(1, 0); public static Type DelegateType => typeof(Func<Tuple<int, int, int, int, int, int>>); public static Tuple<int, int, int, int, int, int> Pack(SeasonState state) { return Tuple.Create((int)state.Season, state.DayInSeason, state.DaysInSeason, state.DayOfYear, state.YearLength, state.Revision); } public static bool TryRead(Tuple<int, int, int, int, int, int>? raw, out SeasonState state) { state = SeasonState.Unavailable; if (raw == null || !SeasonState.Valid(raw.Item1, raw.Item2, raw.Item3, raw.Item4, raw.Item5)) { return false; } state = new SeasonState((SeasonKind)raw.Item1, raw.Item2, raw.Item3, raw.Item4, raw.Item5, raw.Item6); return true; } public static bool TryRead(Func<Tuple<int, int, int, int, int, int>>? provider, out SeasonState state) { state = SeasonState.Unavailable; if (provider == null) { return false; } Tuple<int, int, int, int, int, int> raw; try { raw = provider(); } catch { return false; } return TryRead(raw, out state); } public static SeasonState ReadOrUnavailable(Func<Tuple<int, int, int, int, int, int>>? provider) { if (!TryRead(provider, out var state)) { return SeasonState.Unavailable; } return state; } } public readonly struct SeasonState : IEquatable<SeasonState> { public static SeasonState Unavailable => default(SeasonState); public SeasonKind Season { get; } public int DayInSeason { get; } public int DaysInSeason { get; } public int DayOfYear { get; } public int YearLength { get; } public int Revision { get; } public bool Available => DaysInSeason > 0; public SeasonState(SeasonKind season, int dayInSeason, int daysInSeason, int dayOfYear, int yearLength, int revision) { if (!Valid((int)season, dayInSeason, daysInSeason, dayOfYear, yearLength)) { throw new ArgumentOutOfRangeException("season", "invalid season state"); } Season = season; DayInSeason = dayInSeason; DaysInSeason = daysInSeason; DayOfYear = dayOfYear; YearLength = yearLength; Revision = revision; } internal static bool Valid(int season, int day, int length, int dayOfYear, int year) { if (season < 0 || season > 3) { return false; } if (length >= 0 && year >= length && day >= 0 && day <= length && dayOfYear >= 0) { return dayOfYear <= year; } return false; } public bool Equals(SeasonState other) { if (Season == other.Season && DayInSeason == other.DayInSeason && DaysInSeason == other.DaysInSeason && DayOfYear == other.DayOfYear && YearLength == other.YearLength) { return Revision == other.Revision; } return false; } public override bool Equals(object? obj) { if (obj is SeasonState other) { return Equals(other); } return false; } public override int GetHashCode() { return ((((((((((int)Season * 397) ^ DayInSeason) * 397) ^ DaysInSeason) * 397) ^ DayOfYear) * 397) ^ YearLength) * 397) ^ Revision; } public override string ToString() { if (!Available) { return "seasons unavailable r" + Revision.ToString(CultureInfo.InvariantCulture); } return Season.ToString() + " day " + DayInSeason.ToString(CultureInfo.InvariantCulture) + "/" + DaysInSeason.ToString(CultureInfo.InvariantCulture) + ", year day " + DayOfYear.ToString(CultureInfo.InvariantCulture) + "/" + YearLength.ToString(CultureInfo.InvariantCulture) + " r" + Revision.ToString(CultureInfo.InvariantCulture); } } public static class VanillaSupportContract { public const string Id = "meadworks.admission.vanillasupport"; public static readonly Version ContractVersion = new Version(1, 0); public const int ClientRequired = 0; public const int Mixed = 1; public const int ServerOnlyCapable = 2; public static Type DelegateType => typeof(Func<int>); public static bool Valid(int support) { if (support >= 0) { return support <= 2; } return false; } public static bool TryRead(Func<int>? provider, out int support) { support = 0; if (provider == null) { return false; } int num; try { num = provider(); } catch { return false; } if (!Valid(num)) { return false; } support = num; return true; } } public static class VanillaPeersContract { public const string Id = "meadworks.peers.vanillacount"; public static readonly Version ContractVersion = new Version(1, 0); public const int Unknown = -1; public static Type DelegateType => typeof(Func<int>); public static bool TryRead(Func<int>? provider, out int count) { count = 0; if (provider == null) { return false; } int num; try { num = provider(); } catch { return false; } if (num < 0) { return false; } count = num; return true; } public static bool TryReadAny(Func<int>? provider, out bool any) { int count; bool result = TryRead(provider, out count); any = count > 0; return result; } } public static class PeerKindContract { public const string Id = "meadworks.peers.kind"; public static readonly Version ContractVersion = new Version(1, 0); public const int Unknown = 0; public const int Pending = 1; public const int Crucible = 2; public const int Vanilla = 3; public static Type DelegateType => typeof(Func<long, int>); public static int Read(Func<long, int>? provider, long peerId) { if (provider == null) { return 0; } int num; try { num = provider(peerId); } catch { return 0; } if (num < 0 || num > 3) { return 0; } return num; } } public enum WeatherEventKind { None, Blizzard, Thunderstorm } public enum WeatherEventPhase { Idle, Warning, Active, Clearing } public static class WeatherEventsContract { public const string Id = "meadworks.weather.events"; public static readonly Version ContractVersion = new Version(1, 0); public const float MaximumSecondsLeft = 3600f; public static Type DelegateType => typeof(Func<Tuple<int, int, float, int, bool, int>>); public static Tuple<int, int, float, int, bool, int> Pack(WeatherEventState state) { return Tuple.Create((int)state.Kind, (int)state.Phase, state.SecondsLeft, state.Biomes, state.LocalAffected, state.Revision); } public static bool TryRead(Tuple<int, int, float, int, bool, int>? raw, out WeatherEventState state) { state = WeatherEventState.Idle; if (raw == null || !WeatherEventState.Valid(raw.Item1, raw.Item2, raw.Item3, raw.Item4, raw.Item5)) { return false; } state = new WeatherEventState((WeatherEventKind)raw.Item1, (WeatherEventPhase)raw.Item2, raw.Item3, raw.Item4, raw.Item5, raw.Item6); return true; } public static bool TryRead(Func<Tuple<int, int, float, int, bool, int>>? provider, out WeatherEventState state) { state = WeatherEventState.Idle; if (provider == null) { return false; } Tuple<int, int, float, int, bool, int> raw; try { raw = provider(); } catch { return false; } return TryRead(raw, out state); } } public static class WeatherEventsDetailContract { public const string Id = "meadworks.weather.events.detail"; public static readonly Version ContractVersion = new Version(1, 0); public const int MaximumTokenLength = 32; public static Type DelegateType => typeof(Func<Tuple<int, int, float, int, bool, int, string>>); public static bool TryRead(Tuple<int, int, float, int, bool, int, string>? raw, out WeatherEventDetail detail) { detail = WeatherEventDetail.Idle; if (raw == null || !Valid(raw.Item1, raw.Item2, raw.Item3, raw.Item4, raw.Item5, raw.Item7)) { return false; } detail = new WeatherEventDetail(raw.Item1, (WeatherEventPhase)raw.Item2, raw.Item3, raw.Item4, raw.Item5, raw.Item6, raw.Item7); return true; } public static bool TryRead(Func<Tuple<int, int, float, int, bool, int, string>>? provider, out WeatherEventDetail detail) { detail = WeatherEventDetail.Idle; if (provider == null) { return false; } Tuple<int, int, float, int, bool, int, string> raw; try { raw = provider(); } catch { return false; } return TryRead(raw, out detail); } public static bool ValidToken(string? token) { if (string.IsNullOrEmpty(token) || token.Length > 32) { return false; } foreach (char c in token) { if ((c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '_') { return false; } } return true; } internal static bool Valid(int kind, int phase, float secondsLeft, int biomes, bool localAffected, string? token) { if (kind < 0 || phase < 0 || phase > 3) { return false; } if (float.IsNaN(secondsLeft) || float.IsInfinity(secondsLeft) || secondsLeft < 0f || secondsLeft > 3600f) { return false; } if (biomes < 0) { return false; } if (phase == 0) { if (kind == 0 && secondsLeft == 0f && biomes == 0 && !localAffected) { return string.IsNullOrEmpty(token); } return false; } if (kind > 0) { return ValidToken(token); } return false; } } public readonly struct WeatherEventDetail { public static WeatherEventDetail Idle => new WeatherEventDetail(0, WeatherEventPhase.Idle, 0f, 0, localAffected: false, 0, ""); public int Kind { get; } public WeatherEventPhase Phase { get; } public float SecondsLeft { get; } public int Biomes { get; } public bool LocalAffected { get; } public int Revision { get; } public string Token { get; } public bool Running => Phase != WeatherEventPhase.Idle; public WeatherEventDetail(int kind, WeatherEventPhase phase, float secondsLeft, int biomes, bool localAffected, int revision, string token) { if (!WeatherEventsDetailContract.Valid(kind, (int)phase, secondsLeft, biomes, localAffected, token)) { throw new ArgumentOutOfRangeException("kind", "invalid weather event detail"); } Kind = kind; Phase = phase; SecondsLeft = secondsLeft; Biomes = biomes; LocalAffected = localAffected; Revision = revision; Token = token ?? ""; } public override string ToString() { if (!Running) { return "idle r" + Revision; } return Token + " (" + Kind.ToString(CultureInfo.InvariantCulture) + ") " + Phase.ToString() + " " + SecondsLeft.ToString("0", CultureInfo.InvariantCulture) + "s biomes " + Biomes + (LocalAffected ? " here" : "") + " r" + Revision; } } public readonly struct WeatherEventState : IEquatable<WeatherEventState> { public static WeatherEventState Idle => default(WeatherEventState); public WeatherEventKind Kind { get; } public WeatherEventPhase Phase { get; } public float SecondsLeft { get; } public int Biomes { get; } public bool LocalAffected { get; } public int Revision { get; } public bool Running => Phase != WeatherEventPhase.Idle; public WeatherEventState(WeatherEventKind kind, WeatherEventPhase phase, float secondsLeft, int biomes, bool localAffected, int revision) { if (!Valid((int)kind, (int)phase, secondsLeft, biomes, localAffected)) { throw new ArgumentOutOfRangeException("kind", "invalid weather event state"); } Kind = kind; Phase = phase; SecondsLeft = secondsLeft; Biomes = biomes; LocalAffected = localAffected; Revision = revision; } internal static bool Valid(int kind, int phase, float secondsLeft, int biomes, bool localAffected) { if (kind < 0 || kind > 2) { return false; } if (phase < 0 || phase > 3) { return false; } if (float.IsNaN(secondsLeft) || float.IsInfinity(secondsLeft) || secondsLeft < 0f || secondsLeft > 3600f) { return false; } if (biomes < 0) { return false; } if (phase == 0) { if (kind == 0 && secondsLeft == 0f && biomes == 0) { return !localAffected; } return false; } return kind != 0; } public bool Equals(WeatherEventState other) { if (Kind == other.Kind && Phase == other.Phase && SecondsLeft.Equals(other.SecondsLeft) && Biomes == other.Biomes && LocalAffected == other.LocalAffected) { return Revision == other.Revision; } return false; } public override bool Equals(object? obj) { if (obj is WeatherEventState other) { return Equals(other); } return false; } public override int GetHashCode() { return (int)((((((((uint)((int)Kind * 397) ^ (uint)Phase) * 397) ^ (uint)SecondsLeft.GetHashCode()) * 397) ^ (uint)Biomes) * 397) ^ (uint)Revision) ^ (LocalAffected ? 7919 : 0); } public override string ToString() { if (!Running) { return "idle r" + Revision; } return Kind.ToString() + " " + Phase.ToString() + " " + SecondsLeft.ToString("0", CultureInfo.InvariantCulture) + "s biomes " + Biomes + (LocalAffected ? " here" : "") + " r" + Revision; } } }
plugins/Crucible/Crucible.Core.dll
Decompiled 10 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExtraPotions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")] [assembly: AssemblyFileVersion("0.3.34.0")] [assembly: AssemblyInformationalVersion("0.3.34+fatal: not a git repository (or any of the parent directories): .git")] [assembly: AssemblyProduct("Crucible.Core")] [assembly: AssemblyTitle("Crucible.Core")] [assembly: AssemblyVersion("0.3.34.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 ExtraPotions.Meadworks.Crucible { public enum GuardAction { Move, Merge, Pickup, Sort, StorageCraft, Split, Restock, Float, Cheat } public enum GuardAudit { Clean, Violation, Expected } public static class CheatMarks { public static bool IsQol(GuardAction action) { Validate(action); return action != GuardAction.Cheat; } public static bool Result(GuardAction action, bool anyInputMarked) { return !IsQol(action) || anyInputMarked; } public static bool CanStack(GuardAction action, bool target, bool incoming) { if (IsQol(action)) { return target == incoming; } return true; } public static bool Split(bool sourceMarked) { return sourceMarked; } public static bool StorageCraft(IEnumerable<bool> consumedMarked, bool noCostCheat, bool stationMarked) { if (consumedMarked == null) { throw new ArgumentNullException("consumedMarked"); } if (!(noCostCheat || stationMarked)) { return consumedMarked.Any((bool m) => m); } return true; } public static GuardAudit Audit(GuardAction action, int markedBefore, int markedAfter, int incomingMarked = 0) { if (markedBefore < 0 || markedAfter < 0 || incomingMarked < 0) { throw new ArgumentOutOfRangeException("markedBefore", "Counts must not be negative."); } if (!IsQol(action)) { return GuardAudit.Expected; } if (markedAfter <= markedBefore + incomingMarked) { return GuardAudit.Clean; } return GuardAudit.Violation; } internal static void Validate(GuardAction action) { if (!Enum.IsDefined(typeof(GuardAction), action)) { throw new ArgumentException("Unknown guard action.", "action"); } } } public sealed class GuardScope : IDisposable { private readonly GuardScopes owner; public string Product { get; } public GuardAction Action { get; } public bool? ConsumedMarked { get; private set; } public bool Disposed { get; private set; } internal GuardScope(GuardScopes owner, string product, GuardAction action) { this.owner = owner; Product = product; Action = action; } public void ReportConsumed(bool marked) { ConsumedMarked = ConsumedMarked == true || marked; } internal void Close() { Disposed = true; } public void Dispose() { if (!Disposed) { owner.End(this); } } public override string ToString() { return Product + " " + Action.ToString() + (ConsumedMarked.HasValue ? (" consumed-marked=" + ConsumedMarked.Value) : ""); } } public sealed class GuardScopes { public const int MaxDepth = 32; private readonly List<GuardScope> active = new List<GuardScope>(); public GuardScope? Current { get { if (active.Count != 0) { return active[active.Count - 1]; } return null; } } public int Depth => active.Count; public bool ActiveQol { get { if (Current != null) { return CheatMarks.IsQol(Current.Action); } return false; } } public GuardScope Begin(string product, GuardAction action) { if (string.IsNullOrWhiteSpace(product)) { throw new ArgumentException("Expected a product id.", "product"); } CheatMarks.Validate(action); if (active.Count >= 32) { throw new InvalidOperationException("Too many nested achievement guard scopes; a scope was not disposed."); } GuardScope guardScope = new GuardScope(this, product, action); active.Add(guardScope); return guardScope; } internal void End(GuardScope scope) { active.Remove(scope); scope.Close(); } public void Reset() { foreach (GuardScope item in active) { item.Close(); } active.Clear(); } } public enum ResyncRefusal { None, Disabled, SteppedAside, ProfileUsedCheats, WorldCheated, MarkedItemsHeld } public sealed class ResyncFacts { public bool Enabled { get; } public bool SteppedAside { get; } public bool ProfileUsedCheats { get; } public bool WorldCheated { get; } public bool MarkedItemsHeld { get; } public bool CheatsForgiven { get; } public ResyncFacts(bool enabled, bool steppedAside, bool profileUsedCheats, bool worldCheated, bool markedItemsHeld, bool cheatsForgiven) { Enabled = enabled; SteppedAside = steppedAside; ProfileUsedCheats = profileUsedCheats; WorldCheated = worldCheated; MarkedItemsHeld = markedItemsHeld; CheatsForgiven = cheatsForgiven; } public ResyncFacts With(bool? enabled = null, bool? steppedAside = null, bool? profileUsedCheats = null, bool? worldCheated = null, bool? markedItemsHeld = null, bool? cheatsForgiven = null) { return new ResyncFacts(enabled ?? Enabled, steppedAside ?? SteppedAside, profileUsedCheats ?? ProfileUsedCheats, worldCheated ?? WorldCheated, markedItemsHeld ?? MarkedItemsHeld, cheatsForgiven ?? CheatsForgiven); } } public readonly struct StatChange { public int Stat { get; } public float From { get; } public float To { get; } public StatChange(int stat, float from, float to) { Stat = stat; From = from; To = to; } public override string ToString() { return Stat + ": " + From + " -> " + To; } } public static class StatResync { public const int RawTable = 0; public const int AchievementTable = 1; private const float Epsilon = 0.001f; public static ResyncRefusal Check(ResyncFacts facts) { if (facts == null) { throw new ArgumentNullException("facts"); } if (!facts.Enabled) { return ResyncRefusal.Disabled; } if (facts.SteppedAside) { return ResyncRefusal.SteppedAside; } if (facts.CheatsForgiven) { return ResyncRefusal.None; } if (facts.ProfileUsedCheats) { return ResyncRefusal.ProfileUsedCheats; } if (facts.WorldCheated) { return ResyncRefusal.WorldCheated; } if (facts.MarkedItemsHeld) { return ResyncRefusal.MarkedItemsHeld; } return ResyncRefusal.None; } public static string Describe(ResyncRefusal refusal) { return refusal switch { ResyncRefusal.None => "", ResyncRefusal.Disabled => "Stat resync is turned off.", ResyncRefusal.SteppedAside => "AchievementSync is installed and keeps the achievement stats.", ResyncRefusal.ProfileUsedCheats => "This character has used cheats, so past stats are not copied to achievements.", ResyncRefusal.WorldCheated => "This world uses cheated world modifiers, so past stats are not copied to achievements.", ResyncRefusal.MarkedItemsHeld => "The inventory holds items obtained through cheats, so past stats are not copied to achievements.", _ => throw new ArgumentException("Unknown resync refusal.", "refusal"), }; } public static IReadOnlyList<StatChange> Plan(IReadOnlyDictionary<int, float> raw, IReadOnlyDictionary<int, float> achievement) { if (raw == null) { throw new ArgumentNullException("raw"); } if (achievement == null) { throw new ArgumentNullException("achievement"); } List<StatChange> list = new List<StatChange>(); foreach (KeyValuePair<int, float> item in raw.OrderBy((KeyValuePair<int, float> p) => p.Key)) { float value = item.Value; if (!float.IsNaN(value) && !float.IsInfinity(value) && !(value <= 0f)) { float value2; float num = ((achievement.TryGetValue(item.Key, out value2) && !float.IsNaN(value2) && !float.IsInfinity(value2)) ? value2 : 0f); if (value - num > 0.001f) { list.Add(new StatChange(item.Key, num, value)); } } } return list; } public static uint PlatformValue(float value) { if (float.IsNaN(value) || value <= 0f) { return 0u; } if (value >= 4.2949673E+09f) { return uint.MaxValue; } return (uint)Math.Floor(value); } } public enum PeerClass { RequiredOnAllPeers, ClientOptional, ServerOnly } public static class PeerClasses { public static PeerClass From(MultiplayerPolicy policy) { switch (policy) { case MultiplayerPolicy.RequiredOnAllPeers: return PeerClass.RequiredOnAllPeers; case MultiplayerPolicy.ClientOnly: case MultiplayerPolicy.Optional: return PeerClass.ClientOptional; case MultiplayerPolicy.ServerOnly: return PeerClass.ServerOnly; default: throw new ArgumentException("Unknown multiplayer policy.", "policy"); } } public static PeerClass Of(ProductMetadata product) { return From((product ?? throw new ArgumentNullException("product")).Multiplayer); } } public enum ServerProduct { Unknown, Present, Missing } public sealed class AuthorityFacts { public SessionRole Role { get; } public bool AuthorityReady { get; } public bool IsAdmin { get; } public ServerCrucible Server { get; } public PeerClass PeerClass { get; } public ServerProduct ServerProduct { get; } internal ConfigAuthorityFacts Config => new ConfigAuthorityFacts(Role, AuthorityReady, IsAdmin, Server); public AuthorityFacts(SessionRole role, bool authorityReady, bool isAdmin, ServerCrucible server, PeerClass peerClass, ServerProduct serverProduct = ServerProduct.Unknown) { if (!Enum.IsDefined(typeof(SessionRole), role)) { throw new ArgumentException("Unknown session role.", "role"); } if (!Enum.IsDefined(typeof(ServerCrucible), server)) { throw new ArgumentException("Unknown server state.", "server"); } if (!Enum.IsDefined(typeof(PeerClass), peerClass)) { throw new ArgumentException("Unknown peer class.", "peerClass"); } if (!Enum.IsDefined(typeof(ServerProduct), serverProduct)) { throw new ArgumentException("Unknown server product state.", "serverProduct"); } Role = role; AuthorityReady = authorityReady; IsAdmin = isAdmin; Server = server; PeerClass = peerClass; ServerProduct = serverProduct; } public static AuthorityFacts From(SessionSnapshot session, ServerCrucible server, PeerClass peerClass, ServerProduct serverProduct = ServerProduct.Unknown) { if (session == null) { throw new ArgumentNullException("session"); } return new AuthorityFacts(session.Role, session.AuthorityReady, session.IsAdmin, server, peerClass, serverProduct); } } public sealed class AuthorityDecision { public bool Allowed { get; } public ConfigBlockReason Reason { get; } public ConfigValueSource Source { get; } public bool RequiresServerAmendment { get; } public bool ProductActive { get; } public ConfigScope Scope { get; } public bool MayUse { get { if (ProductActive) { return Source != ConfigValueSource.Locked; } return false; } } public string Message => AuthorityGate.Describe(Reason); internal AuthorityDecision(ConfigScope scope, bool allowed, ConfigBlockReason reason, ConfigValueSource source, bool amend, bool active) { Scope = scope; Allowed = allowed; Reason = reason; Source = source; RequiresServerAmendment = amend; ProductActive = active; } internal static AuthorityDecision From(ConfigScope scope, ConfigEditDecision d) { return new AuthorityDecision(scope, d.Allowed, d.Reason, d.Source, d.RequiresServerAmendment, active: true); } public T Effective<T>(ScopedValue<T> value) { return (value ?? throw new ArgumentNullException("value")).Effective(Source); } public override string ToString() { return (Allowed ? "Allowed" : ("Blocked:" + Reason)) + " source=" + Source.ToString() + (RequiresServerAmendment ? " amend" : "") + (ProductActive ? "" : " product-off"); } } public static class AuthorityGate { public static bool ProductActive(AuthorityFacts facts, out ConfigBlockReason reason) { if (facts == null) { throw new ArgumentNullException("facts"); } reason = ConfigBlockReason.None; if (facts.Role != SessionRole.RemoteClient) { return true; } if (facts.PeerClass == PeerClass.ServerOnly) { reason = ConfigBlockReason.ServerOnly; } else if (facts.PeerClass == PeerClass.RequiredOnAllPeers && facts.Server == ServerCrucible.Absent) { reason = ConfigBlockReason.VanillaServer; } else if (facts.PeerClass == PeerClass.RequiredOnAllPeers && facts.ServerProduct == ServerProduct.Missing) { reason = ConfigBlockReason.ProductDisabled; } return reason == ConfigBlockReason.None; } public static AuthorityDecision Decide(ConfigScope scope, AuthorityFacts facts, bool serverLocked = true) { if (!Enum.IsDefined(typeof(ConfigScope), scope)) { throw new ArgumentException("Unknown configuration scope.", "scope"); } if (facts == null) { throw new ArgumentNullException("facts"); } bool flag = scope == ConfigScope.Cheat; if (scope == ConfigScope.Local || facts.Role != SessionRole.RemoteClient) { return AuthorityDecision.From(scope, ConfigEditPolicy.Decide(scope, facts.Config, serverLocked)); } if (!ProductActive(facts, out var reason)) { return new AuthorityDecision(scope, allowed: false, reason, flag ? ConfigValueSource.Locked : ConfigValueSource.Own, amend: false, active: false); } bool flag2 = facts.PeerClass == PeerClass.ClientOptional; if (flag2 && facts.ServerProduct == ServerProduct.Missing) { if (!flag) { return new AuthorityDecision(scope, allowed: true, ConfigBlockReason.None, ConfigValueSource.Own, amend: false, active: true); } return new AuthorityDecision(scope, allowed: false, ConfigBlockReason.ProductDisabled, ConfigValueSource.Locked, amend: false, active: true); } return AuthorityDecision.From(scope, ConfigEditPolicy.Decide(scope, facts.Config, !flag2 || serverLocked)); } public static string Describe(ConfigBlockReason reason) { return BlockedOptionText.English(reason); } } public static class CheatDefaults { public static string? Review<T>(string key, T defaultValue, T safeValue) { if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentException("Expected a setting key.", "key"); } if (EqualityComparer<T>.Default.Equals(defaultValue, safeValue)) { return null; } return key + " defaults to " + Convert.ToString(defaultValue, CultureInfo.InvariantCulture) + "; a cheat must default to its safe value " + Convert.ToString(safeValue, CultureInfo.InvariantCulture) + "."; } public static string? ReviewCategory(string key, MischiefCategory? category) { if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentException("Expected a setting key.", "key"); } if (!category.HasValue) { return key + " has no Mischief category; it is treated as " + MischiefCategory.World.ToString() + " (strictest). Bind it with a category."; } return null; } } public sealed class ServerPresenceTimer { private double started; private double timeout; public double TimeoutSeconds { get { return timeout; } set { if (double.IsNaN(value) || value <= 0.0 || value > 300.0) { throw new ArgumentOutOfRangeException("value", "Timeout must be in (0, 300] seconds."); } timeout = value; } } public ServerCrucible State { get; private set; } public bool Waiting { get; private set; } public ServerPresenceTimer(double timeoutSeconds = 10.0) { TimeoutSeconds = timeoutSeconds; } public void Connect(double now) { if (!Waiting) { State = ServerCrucible.Unknown; Waiting = true; started = now; } } public void Hello() { State = ServerCrucible.Present; Waiting = false; } public void Disconnect() { State = ServerCrucible.Unknown; Waiting = false; } public ServerCrucible Tick(double now) { if (Waiting && now - started >= TimeoutSeconds) { State = ServerCrucible.Absent; Waiting = false; } return State; } public static ServerCrucible For(SessionRole role, ServerCrucible timerState, bool authorityReady) { if (role == SessionRole.Menu) { return ServerCrucible.Unknown; } if (role != SessionRole.RemoteClient || authorityReady) { return ServerCrucible.Present; } return timerState; } } public sealed class AuthorityMemo { private sealed class Entry { public SessionRole Role; public bool Ready; public bool Admin; public ServerCrucible Server; public PeerClass Peer; public ServerProduct ServerProduct; public AuthorityFacts Facts; public readonly AuthorityDecision?[] Decisions = new AuthorityDecision[6]; } private const int MaxEntries = 512; private const int ScopeCount = 3; private readonly ProductRegistry registry; private readonly Dictionary<string, Entry> entries = new Dictionary<string, Entry>(StringComparer.Ordinal); public AuthorityMemo(ProductRegistry registry) { this.registry = registry ?? throw new ArgumentNullException("registry"); } public bool IsFor(ProductRegistry other) { return registry == other; } public PeerClass PeerClassOf(string productId) { if (!registry.TryGetMetadata(productId ?? "", out ProductMetadata metadata)) { return PeerClass.RequiredOnAllPeers; } return PeerClasses.Of(metadata); } public AuthorityFacts Facts(SessionSnapshot session, ServerCrucible server, string productId, ServerProduct serverProduct) { return Resolve(session, server, productId, serverProduct).Facts; } public bool ProductActive(SessionSnapshot session, ServerCrucible server, string productId, ServerProduct serverProduct, out ConfigBlockReason reason) { return AuthorityGate.ProductActive(Resolve(session, server, productId, serverProduct).Facts, out reason); } public AuthorityDecision Decide(SessionSnapshot session, ServerCrucible server, string productId, ServerProduct serverProduct, ConfigScope scope, bool serverLocked = true) { Entry entry = Resolve(session, server, productId, serverProduct); int num = (int)scope; if ((uint)num >= 3u) { return AuthorityGate.Decide(scope, entry.Facts, serverLocked); } num = num * 2 + (serverLocked ? 1 : 0); return entry.Decisions[num] ?? (entry.Decisions[num] = AuthorityGate.Decide(scope, entry.Facts, serverLocked)); } private Entry Resolve(SessionSnapshot session, ServerCrucible server, string productId, ServerProduct serverProduct) { if (session == null) { throw new ArgumentNullException("session"); } string text = productId ?? ""; PeerClass peerClass = PeerClassOf(text); if (entries.TryGetValue(text, out Entry value)) { if (value.Role == session.Role && value.Ready == session.AuthorityReady && value.Admin == session.IsAdmin && value.Server == server && value.Peer == peerClass && value.ServerProduct == serverProduct) { return value; } } else { if (entries.Count >= 512) { entries.Clear(); } value = new Entry(); } AuthorityFacts facts = AuthorityFacts.From(session, server, peerClass, serverProduct); value.Role = session.Role; value.Ready = session.AuthorityReady; value.Admin = session.IsAdmin; value.Server = server; value.Peer = peerClass; value.ServerProduct = serverProduct; value.Facts = facts; Array.Clear(value.Decisions, 0, value.Decisions.Length); entries[text] = value; return value; } } public enum MischiefCategory { Personal, Economy, World, Information, Travel, Progress } public enum MischiefAccess { Nobody, Admins, Listed, Everyone } public static class MischiefCategories { public const MischiefCategory Untagged = MischiefCategory.World; public static IReadOnlyList<MischiefCategory> All { get; } = new MischiefCategory[6] { MischiefCategory.Personal, MischiefCategory.Economy, MischiefCategory.World, MischiefCategory.Information, MischiefCategory.Travel, MischiefCategory.Progress }; public static bool TryParse(string? text, out MischiefCategory category) { category = MischiefCategory.World; string text2 = text?.Trim(); if (string.IsNullOrEmpty(text2) || char.IsDigit(text2[0]) || text2[0] == '-') { return false; } if (Enum.TryParse<MischiefCategory>(text2, ignoreCase: true, out category)) { return Enum.IsDefined(typeof(MischiefCategory), category); } return false; } internal static void Validate(MischiefCategory category) { if (!Enum.IsDefined(typeof(MischiefCategory), category)) { throw new ArgumentException("Unknown cheat category.", "category"); } } } public sealed class MischiefPlayers { public const int MaxEntries = 512; private const string Steam = "Steam"; private readonly HashSet<string> ids; private readonly string[] ordered; public static MischiefPlayers Empty { get; } = new MischiefPlayers(Array.Empty<string>()); public int Count => ordered.Length; public IReadOnlyList<string> Ids => ordered; private MischiefPlayers(IEnumerable<string> normalized) { ordered = normalized.Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string x) => x, StringComparer.Ordinal).ToArray(); ids = new HashSet<string>(ordered, StringComparer.Ordinal); } public static string? Normalize(string? id) { string text = id?.Trim(); if (string.IsNullOrEmpty(text) || text.Length > 96) { return null; } string text2 = text; foreach (char c in text2) { if (!char.IsLetterOrDigit(c) && c != '_' && c != '-' && c != '.') { return null; } } int num = text.IndexOf('_'); if (num < 0) { if (!text.All(char.IsDigit)) { return null; } return "Steam_" + text; } string text3 = text.Substring(0, num); string text4 = text.Substring(num + 1); if (text3.Length == 0 || text4.Length == 0) { return null; } if (string.Equals(text3, "Steam", StringComparison.OrdinalIgnoreCase)) { text3 = "Steam"; } return text3 + "_" + text4; } public static MischiefPlayers Parse(string? text, out IReadOnlyList<string> rejected) { List<string> list = new List<string>(); List<string> list2 = new List<string>(); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); string[] array = (text ?? "").Split(new char[4] { ',', ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0) { continue; } string text3 = Normalize(text2); if (text3 == null) { list.Add(text2); } else if (!hashSet.Contains(text3)) { if (list2.Count >= 512) { list.Add(text2); continue; } hashSet.Add(text3); list2.Add(text3); } } rejected = list; if (list2.Count != 0) { return new MischiefPlayers(list2); } return Empty; } public bool Contains(string? platformId) { string text = Normalize(platformId); if (text != null) { return ids.Contains(text); } return false; } public string Format() { return string.Join(",", ordered); } public override string ToString() { return Format(); } } public sealed class MischiefPolicy { public const MischiefAccess DefaultAccess = MischiefAccess.Admins; public const string Section = "Mischief"; public const string ListedKey = "ListedPlayers"; private readonly MischiefAccess[] access; public MischiefPlayers Listed { get; } public static MischiefPolicy Default { get; } = new MischiefPolicy(MischiefCategories.All.Select((MischiefCategory _) => MischiefAccess.Admins).ToArray(), MischiefPlayers.Empty); private MischiefPolicy(MischiefAccess[] access, MischiefPlayers listed) { this.access = access; Listed = listed; } public MischiefAccess Access(MischiefCategory category) { MischiefCategories.Validate(category); return access[(int)category]; } public MischiefPolicy With(MischiefCategory category, MischiefAccess value) { MischiefCategories.Validate(category); if (!Enum.IsDefined(typeof(MischiefAccess), value)) { throw new ArgumentException("Unknown access level.", "value"); } MischiefAccess[] obj = (MischiefAccess[])access.Clone(); obj[(int)category] = value; return new MischiefPolicy(obj, Listed); } public MischiefPolicy WithListed(MischiefPlayers listed) { return new MischiefPolicy(access, listed ?? throw new ArgumentNullException("listed")); } public static string SettingKey(MischiefCategory category) { MischiefCategories.Validate(category); return category.ToString(); } public static MischiefPolicy FromSettings(Func<string, string?> read, string? listed, out IReadOnlyList<string> invalid) { if (read == null) { throw new ArgumentNullException("read"); } List<string> list = new List<string>(); MischiefAccess[] array = new MischiefAccess[MischiefCategories.All.Count]; foreach (MischiefCategory item in MischiefCategories.All) { string text = read(SettingKey(item))?.Trim(); MischiefAccess mischiefAccess = MischiefAccess.Admins; if (!string.IsNullOrEmpty(text)) { if (!char.IsDigit(text[0]) && Enum.TryParse<MischiefAccess>(text, ignoreCase: true, out var result) && Enum.IsDefined(typeof(MischiefAccess), result)) { mischiefAccess = result; } else { list.Add(SettingKey(item) + "=" + text); } } array[(int)item] = mischiefAccess; } IReadOnlyList<string> rejected; MischiefPlayers listed2 = MischiefPlayers.Parse(listed, out rejected); foreach (string item2 in rejected) { list.Add("ListedPlayers: " + item2); } invalid = list; return new MischiefPolicy(array, listed2); } public bool Grants(MischiefCategory category, bool isAdmin, string? platformId, out ConfigBlockReason reason) { reason = ConfigBlockReason.None; switch (Access(category)) { case MischiefAccess.Everyone: return true; case MischiefAccess.Listed: if (isAdmin || Listed.Contains(platformId)) { return true; } reason = ConfigBlockReason.MischiefDenied; return false; case MischiefAccess.Admins: if (isAdmin) { return true; } reason = ConfigBlockReason.NotAdmin; return false; default: reason = ConfigBlockReason.MischiefDenied; return false; } } public override string ToString() { return string.Join(" ", MischiefCategories.All.Select((MischiefCategory c) => c.ToString() + "=" + access[(int)c])) + " listed=" + Listed.Count; } } public sealed class MischiefDecision { public MischiefCategory Category { get; } public bool Allowed { get; } public ConfigBlockReason Reason { get; } public ConfigValueSource Source { get; } public bool RequiresServerAmendment { get; } public bool ProductActive { get; } public MischiefAccess? Access { get; } public AuthorityDecision Authority { get; } public string Message => AuthorityGate.Describe(Reason); internal MischiefDecision(MischiefCategory category, bool allowed, ConfigBlockReason reason, ConfigValueSource source, bool amend, bool active, MischiefAccess? access, AuthorityDecision authority) { Category = category; Allowed = allowed; Reason = reason; Source = source; RequiresServerAmendment = amend; ProductActive = active; Access = access; Authority = authority; } public T Effective<T>(ScopedValue<T> value) { return (value ?? throw new ArgumentNullException("value")).Effective(Source); } public override string ToString() { return Category.ToString() + ": " + (Allowed ? "Allowed" : ("Blocked:" + Reason)) + " source=" + Source.ToString() + (Access.HasValue ? (" policy=" + Access.Value) : "") + (RequiresServerAmendment ? " amend" : "") + (ProductActive ? "" : " product-off"); } } public static class MischiefGate { public static MischiefDecision Decide(MischiefCategory category, AuthorityFacts facts, MischiefPolicy policy, string? platformId = null) { MischiefCategories.Validate(category); if (facts == null) { throw new ArgumentNullException("facts"); } if (policy == null) { throw new ArgumentNullException("policy"); } AuthorityDecision authorityDecision = AuthorityGate.Decide(ConfigScope.Cheat, facts); if (facts.Role != SessionRole.RemoteClient) { return new MischiefDecision(category, authorityDecision.Allowed, authorityDecision.Reason, authorityDecision.Source, authorityDecision.RequiresServerAmendment, authorityDecision.ProductActive, null, authorityDecision); } if (!authorityDecision.Allowed && authorityDecision.Reason != ConfigBlockReason.NotAdmin) { return new MischiefDecision(category, allowed: false, authorityDecision.Reason, ConfigValueSource.Locked, amend: false, authorityDecision.ProductActive, null, authorityDecision); } MischiefAccess value = policy.Access(category); if (!policy.Grants(category, facts.IsAdmin, platformId, out var reason)) { return new MischiefDecision(category, allowed: false, reason, ConfigValueSource.Locked, amend: false, authorityDecision.ProductActive, value, authorityDecision); } return new MischiefDecision(category, allowed: true, ConfigBlockReason.None, ConfigValueSource.Server, authorityDecision.Allowed && authorityDecision.RequiresServerAmendment, authorityDecision.ProductActive, value, authorityDecision); } public static MischiefDecision DecideForPeer(MischiefCategory category, MischiefPolicy policy, bool peerIsAdmin, string? peerPlatformId) { return Decide(category, new AuthorityFacts(SessionRole.RemoteClient, authorityReady: true, peerIsAdmin, ServerCrucible.Present, PeerClass.RequiredOnAllPeers, ServerProduct.Present), policy, peerPlatformId); } } public sealed class MischiefCheats { private readonly object gate = new object(); private readonly Dictionary<(string Owner, string Key), MischiefCategory?> tags = new Dictionary<(string, string), MischiefCategory?>(); public int Count { get { lock (gate) { return tags.Count; } } } public void Tag(string owner, string key, MischiefCategory? category) { if (string.IsNullOrWhiteSpace(owner)) { throw new ArgumentException("Expected an owner.", "owner"); } if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentException("Expected a setting key.", "key"); } if (category.HasValue) { MischiefCategories.Validate(category.Value); } lock (gate) { tags[(owner, key)] = category; } } public bool Contains(string owner, string key) { lock (gate) { return tags.ContainsKey((owner ?? "", key ?? "")); } } public MischiefCategory CategoryOf(string owner, string key) { lock (gate) { MischiefCategory? value; return (tags.TryGetValue((owner ?? "", key ?? ""), out value) && value.HasValue) ? value.Value : MischiefCategory.World; } } public IReadOnlyList<string> Review() { lock (gate) { return (from t in tags where !t.Value.HasValue select CheatDefaults.ReviewCategory(t.Key.Owner + " " + t.Key.Key, null)).OrderBy<string, string>((string x) => x, StringComparer.Ordinal).ToArray(); } } } public sealed class MischiefMemo { private sealed class Slot { public AuthorityFacts? Facts; public MischiefPolicy? Policy; public string? PlatformId; public MischiefDecision? Decision; } private const int MaxEntries = 512; private const int CategoryCount = 6; private readonly Dictionary<string, Slot?[]> decisions = new Dictionary<string, Slot[]>(StringComparer.Ordinal); private readonly Dictionary<(string Product, string Cheat), (MischiefDecision Decision, BlockedOption Status)> statuses = new Dictionary<(string, string), (MischiefDecision, BlockedOption)>(); public MischiefDecision Decide(string productId, MischiefCategory category, AuthorityFacts facts, MischiefPolicy policy, Func<string?> platformId) { if ((uint)category >= 6u || productId == null || facts == null || policy == null || platformId == null) { return MischiefGate.Decide(category, facts, policy, platformId?.Invoke()); } string text = ((facts.Role == SessionRole.RemoteClient) ? platformId() : null); if (!decisions.TryGetValue(productId, out Slot[] value)) { if (decisions.Count >= 512) { decisions.Clear(); } value = (decisions[productId] = new Slot[6]); } Slot slot = value[(int)category] ?? (value[(int)category] = new Slot()); if (slot.Decision != null && slot.Facts == facts && slot.Policy == policy && string.Equals(slot.PlatformId, text, StringComparison.Ordinal)) { return slot.Decision; } MischiefDecision mischiefDecision = MischiefGate.Decide(category, facts, policy, text); slot.Facts = facts; slot.Policy = policy; slot.PlatformId = text; slot.Decision = mischiefDecision; return mischiefDecision; } public BlockedOption Query(string productId, string cheat, MischiefDecision decision) { if (productId == null || cheat == null || decision == null) { return BlockedOption.From(productId, cheat, decision); } (string, string) key = (productId, cheat); if (statuses.TryGetValue(key, out (MischiefDecision, BlockedOption) value) && value.Item1 == decision) { return value.Item2; } BlockedOption blockedOption = BlockedOption.From(productId, cheat, decision); if (statuses.Count >= 512) { statuses.Clear(); } statuses[key] = (decision, blockedOption); return blockedOption; } } public sealed class PeerProduct { public string Id { get; } public string Name { get; } public Version? Version { get; } public PeerClass PeerClass { get; } public bool Legacy => Version == null; public string VersionText { get { if (!(Version == null)) { return Version.Major.ToString(CultureInfo.InvariantCulture) + "." + Version.Minor.ToString(CultureInfo.InvariantCulture); } return "unknown version"; } } public PeerProduct(string id, string name, Version? version, PeerClass peerClass) { Validation.Id(id); if (string.IsNullOrWhiteSpace(name) || name.Length > 128 || name.IndexOfAny(new char[3] { '\0', '\r', '\n' }) >= 0) { throw new ArgumentException("Invalid product name.", "name"); } if (version != null && (version.Major < 0 || version.Minor < 0 || version.Build >= 0 || version.Revision >= 0)) { throw new ArgumentException("Wire versions are major.minor.", "version"); } if (!Enum.IsDefined(typeof(PeerClass), peerClass)) { throw new ArgumentException("Unknown peer class.", "peerClass"); } Id = id; Name = name; Version = version; PeerClass = peerClass; } public static PeerProduct From(ProductMetadata product) { if (product == null) { throw new ArgumentNullException("product"); } return new PeerProduct(product.Id, product.Name, new Version(product.Version.Major, product.Version.Minor), PeerClasses.Of(product)); } } public sealed class CapabilitySet { public int FormatVersion { get; } public IReadOnlyList<PeerProduct> Products { get; } internal CapabilitySet(int format, IReadOnlyList<PeerProduct> products) { FormatVersion = format; Products = products; } } public static class CapabilityCodec { public const int CurrentFormat = 2; private const int MaxBytes = 49152; private const int MaxProducts = 256; private static readonly UTF8Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); public static byte[] Encode(IEnumerable<PeerProduct> products) { List<PeerProduct> list = Distinct(products); if (list.Any((PeerProduct p) => p.Legacy)) { throw new ArgumentException("Legacy entries have no version to encode.", "products"); } using MemoryStream memoryStream = new MemoryStream(); using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Utf8, leaveOpen: true)) { binaryWriter.Write(-2); binaryWriter.Write(list.Count); foreach (PeerProduct item in list) { binaryWriter.Write(item.Id); binaryWriter.Write(item.Name); binaryWriter.Write(item.Version.Major); binaryWriter.Write(item.Version.Minor); binaryWriter.Write((byte)item.PeerClass); } } if (memoryStream.Length > 49152) { throw new ArgumentException("Capability frame exceeds bounds.", "products"); } return memoryStream.ToArray(); } public static byte[] EncodeV1(IEnumerable<string> productIds) { List<string> list = (productIds ?? throw new ArgumentNullException("productIds")).ToList(); if (list.Count > 256) { throw new ArgumentException("Too many products.", "productIds"); } using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true); binaryWriter.Write(list.Count); foreach (string item in list) { binaryWriter.Write(item); binaryWriter.Write(1); binaryWriter.Write(0); } binaryWriter.Flush(); return memoryStream.ToArray(); } public static CapabilitySet Decode(byte[] bytes) { if (bytes == null || bytes.Length > 49152 || bytes.Length < 4) { throw new InvalidDataException("Capability frame exceeds bounds."); } try { using MemoryStream memoryStream = new MemoryStream(bytes, writable: false); using BinaryReader binaryReader = new BinaryReader(memoryStream, Utf8); int num = binaryReader.ReadInt32(); CapabilitySet result; if (num >= 0) { result = new CapabilitySet(1, ReadV1(binaryReader, num)); } else { if (num != -2) { throw new NotSupportedException("Capability format " + (-num).ToString(CultureInfo.InvariantCulture) + " is newer than this Crucible understands; update Crucible."); } result = new CapabilitySet(2, ReadV2(binaryReader)); } if (memoryStream.Position != memoryStream.Length) { throw new InvalidDataException("Trailing capability data."); } return result; } catch (Exception ex) when (ex is EndOfStreamException || ex is ArgumentException || ex is DecoderFallbackException || ex is FormatException) { throw new InvalidDataException("Malformed capability frame.", ex); } } private static IReadOnlyList<PeerProduct> ReadV1(BinaryReader reader, int count) { if (count > 256) { throw new InvalidDataException("Too many products."); } List<PeerProduct> list = new List<PeerProduct>(count); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < count; i++) { string text = reader.ReadString(); int num = reader.ReadInt32(); int num2 = reader.ReadInt32(); if (num != 1 || num2 != 0) { throw new InvalidDataException("Protocol mismatch."); } if (!hashSet.Add(text)) { throw new InvalidDataException("Duplicate product."); } list.Add(new PeerProduct(text, text, null, PeerClass.RequiredOnAllPeers)); } return list.AsReadOnly(); } private static IReadOnlyList<PeerProduct> ReadV2(BinaryReader reader) { int num = reader.ReadInt32(); if (num < 0 || num > 256) { throw new InvalidDataException("Invalid product count."); } List<PeerProduct> list = new List<PeerProduct>(num); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < num; i++) { string text = reader.ReadString(); string name = reader.ReadString(); int num2 = reader.ReadInt32(); int num3 = reader.ReadInt32(); byte b = reader.ReadByte(); if (num2 < 0 || num3 < 0) { throw new InvalidDataException("Invalid version."); } if (!Enum.IsDefined(typeof(PeerClass), (int)b)) { throw new InvalidDataException("Unknown peer class."); } if (!hashSet.Add(text)) { throw new InvalidDataException("Duplicate product."); } list.Add(new PeerProduct(text, name, new Version(num2, num3), (PeerClass)b)); } return list.AsReadOnly(); } internal static List<PeerProduct> Distinct(IEnumerable<PeerProduct> products) { List<PeerProduct> list = (products ?? throw new ArgumentNullException("products")).ToList(); if (list.Any((PeerProduct p) => p == null)) { throw new ArgumentException("Null product.", "products"); } if (list.Count > 256) { throw new ArgumentException("Too many products.", "products"); } if (list.Select((PeerProduct p) => p.Id).Distinct<string>(StringComparer.Ordinal).Count() != list.Count) { throw new ArgumentException("Duplicate product id.", "products"); } return list.OrderBy<PeerProduct, string>((PeerProduct p) => p.Id, StringComparer.Ordinal).ToList(); } } public enum PeerProductOutcome { Match, VersionUnverified, MissingOnClient, VersionMismatch, MissingOnServer, ClientOnly, Unsynced, ServerOnlyIgnored } public sealed class PeerProductResult { public string Id { get; } public string Name { get; } public PeerProductOutcome Outcome { get; } public PeerProduct? Server { get; } public PeerProduct? Client { get; } public string? Message { get; } public bool Refuses { get { if (Outcome != PeerProductOutcome.MissingOnClient) { return Outcome == PeerProductOutcome.VersionMismatch; } return true; } } internal PeerProductResult(PeerProduct? server, PeerProduct? client, PeerProductOutcome outcome, string? message) { PeerProduct peerProduct = server ?? client; Id = peerProduct.Id; Name = peerProduct.Name; Server = server; Client = client; Outcome = outcome; Message = message; } } public sealed class PeerClassVerdict { public IReadOnlyList<PeerProductResult> Products { get; } public bool Admit => RefusalLines.Count == 0; public IReadOnlyList<string> RefusalLines { get; } public IReadOnlyList<string> Notices { get; } public IReadOnlyList<string> DisabledForSession { get; } internal PeerClassVerdict(List<PeerProductResult> products) { Products = products.AsReadOnly(); RefusalLines = (from p in products where p.Refuses select p.Message).ToList().AsReadOnly(); Notices = (from p in products where !p.Refuses && p.Message != null select p.Message).ToList().AsReadOnly(); DisabledForSession = (from p in products where p.Outcome == PeerProductOutcome.MissingOnServer select p.Id).ToList().AsReadOnly(); } public ServerProduct ServerStateOf(string productId) { PeerProductResult peerProductResult = Products.FirstOrDefault((PeerProductResult p) => p.Id == productId); if (peerProductResult == null) { return ServerProduct.Unknown; } switch (peerProductResult.Outcome) { case PeerProductOutcome.MissingOnServer: case PeerProductOutcome.ClientOnly: case PeerProductOutcome.Unsynced: return ServerProduct.Missing; case PeerProductOutcome.MissingOnClient: return ServerProduct.Unknown; default: return ServerProduct.Present; } } public string RefusalMessage(string code) { if (code == null || !Regex.IsMatch(code, "^[a-z0-9]{6}$")) { throw new ArgumentException("Refusal codes are six characters a-z0-9.", "code"); } return string.Join("\n", RefusalLines) + "\nRefusal code " + code + "; the same code is in the server's log."; } } public static class PeerClassPolicy { public static PeerClassVerdict Evaluate(IEnumerable<PeerProduct> server, IEnumerable<PeerProduct> client) { List<PeerProduct> source = CapabilityCodec.Distinct(server ?? throw new ArgumentNullException("server")); List<PeerProduct> source2 = CapabilityCodec.Distinct(client ?? throw new ArgumentNullException("client")); List<PeerProductResult> list = new List<PeerProductResult>(); foreach (string id in source.Select((PeerProduct p) => p.Id).Union<string>(source2.Select((PeerProduct p) => p.Id), StringComparer.Ordinal).OrderBy<string, string>((string i) => i, StringComparer.Ordinal)) { PeerProduct peerProduct = source.FirstOrDefault((PeerProduct p) => p.Id == id); PeerProduct c = source2.FirstOrDefault((PeerProduct p) => p.Id == id); PeerProductResult peerProductResult = ((peerProduct != null) ? ServerSide(peerProduct, c) : ClientSide(c)); if (peerProductResult != null) { list.Add(peerProductResult); } } return new PeerClassVerdict(list); } private static PeerProductResult? ServerSide(PeerProduct s, PeerProduct? c) { if (s.PeerClass == PeerClass.ServerOnly) { if (c != null) { return new PeerProductResult(s, c, PeerProductOutcome.ServerOnlyIgnored, null); } return null; } if (c == null) { if (s.PeerClass != PeerClass.RequiredOnAllPeers) { return null; } return new PeerProductResult(s, null, PeerProductOutcome.MissingOnClient, "This server requires " + s.Name + " " + s.VersionText + ". Install " + s.Name + " " + s.VersionText + " to join."); } if (s.Legacy || c.Legacy) { return new PeerProductResult(s, c, PeerProductOutcome.VersionUnverified, null); } if (s.Version == c.Version) { return new PeerProductResult(s, c, PeerProductOutcome.Match, null); } string text = "This server runs " + s.Name + " " + s.VersionText + "; you have " + c.VersionText + ". "; if (s.PeerClass == PeerClass.ClientOptional) { return new PeerProductResult(s, c, PeerProductOutcome.Unsynced, text + s.Name + " uses your own settings until the versions match."); } return new PeerProductResult(s, c, PeerProductOutcome.VersionMismatch, text + ((c.Version < s.Version) ? ("Update " + s.Name + " to " + s.VersionText + " to join.") : ("Install " + s.Name + " " + s.VersionText + " to join, or ask the host to update."))); } private static PeerProductResult ClientSide(PeerProduct c) { return c.PeerClass switch { PeerClass.RequiredOnAllPeers => new PeerProductResult(null, c, PeerProductOutcome.MissingOnServer, "This server does not run " + c.Name + "; " + c.Name + " is turned off for this session."), PeerClass.ClientOptional => new PeerProductResult(null, c, PeerProductOutcome.ClientOnly, null), _ => new PeerProductResult(null, c, PeerProductOutcome.ServerOnlyIgnored, null), }; } } public static class RefusalCodes { private const string Alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"; public static string New() { byte[] array = new byte[6]; using (RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create()) { randomNumberGenerator.GetBytes(array); } char[] array2 = new char[6]; for (int i = 0; i < 6; i++) { array2[i] = "abcdefghijklmnopqrstuvwxyz0123456789"[array[i] % "abcdefghijklmnopqrstuvwxyz0123456789".Length]; } return new string(array2); } } public static class PeerInfoGateTarget { public const string JotunnType = "Jotunn.Utils.ModCompatibility"; public const string JotunnMethod = "RPC_Jotunn_ReceiveVersionData"; public static bool Fits(bool isStatic, IReadOnlyList<string> parameterTypes) { if (isStatic && parameterTypes != null && parameterTypes.Count == 2 && parameterTypes[0] == "ZRpc") { return parameterTypes[1] == "ZPackage"; } return false; } } public sealed class RefusalNotice { public const int Format = 1; public const int MaxLines = 64; public const int MaxLineLength = 512; public const int MaxBytes = 32768; private static readonly Regex CodePattern = new Regex("^[a-z0-9]{6}$", RegexOptions.CultureInvariant); private static readonly UTF8Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); public string Code { get; } public IReadOnlyList<string> Lines { get; } public string Message => string.Join("\n", Lines) + "\nRefusal code " + Code + "; the same code is in the server's log."; public RefusalNotice(string code, IEnumerable<string> lines) { if (code == null || !CodePattern.IsMatch(code)) { throw new ArgumentException("Refusal codes are six characters a-z0-9.", "code"); } List<string> list = (lines ?? throw new ArgumentNullException("lines")).ToList(); if (list.Count == 0 || list.Count > 64) { throw new ArgumentException("A refusal names 1 to " + 64 + " reasons.", "lines"); } foreach (string item in list) { if (string.IsNullOrWhiteSpace(item) || item.Length > 512 || item.IndexOfAny(new char[3] { '\r', '\n', '\0' }) >= 0) { throw new ArgumentException("Refusal lines are single non-empty lines of at most " + 512 + " characters.", "lines"); } } Code = code; Lines = list.AsReadOnly(); } public static RefusalNotice From(PeerClassVerdict verdict, string code) { if (verdict == null) { throw new ArgumentNullException("verdict"); } if (verdict.Admit) { throw new ArgumentException("An admitted verdict has no refusal notice.", "verdict"); } return new RefusalNotice(code, verdict.RefusalLines); } public byte[] Encode() { using MemoryStream memoryStream = new MemoryStream(); using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Utf8, leaveOpen: true)) { binaryWriter.Write(1); binaryWriter.Write(Code); binaryWriter.Write(Lines.Count); foreach (string line in Lines) { binaryWriter.Write(line); } } if (memoryStream.Length > 32768) { throw new InvalidOperationException("Refusal notice exceeds bounds."); } return memoryStream.ToArray(); } public static RefusalNotice Decode(byte[] bytes) { if (bytes == null || bytes.Length < 4 || bytes.Length > 32768) { throw new InvalidDataException("Refusal notice exceeds bounds."); } try { using MemoryStream memoryStream = new MemoryStream(bytes, writable: false); using BinaryReader binaryReader = new BinaryReader(memoryStream, Utf8); int num = binaryReader.ReadInt32(); if (num > 1) { throw new NotSupportedException("Refusal notice format " + num + " is newer than this Crucible understands; update Crucible."); } if (num != 1) { throw new InvalidDataException("Unknown refusal notice format."); } string code = binaryReader.ReadString(); int num2 = binaryReader.ReadInt32(); if (num2 < 1 || num2 > 64) { throw new InvalidDataException("Invalid refusal line count."); } List<string> list = new List<string>(num2); for (int i = 0; i < num2; i++) { list.Add(binaryReader.ReadString()); } if (memoryStream.Position != memoryStream.Length) { throw new InvalidDataException("Trailing refusal data."); } return new RefusalNotice(code, list); } catch (Exception ex) when (ex is EndOfStreamException || ex is ArgumentException || ex is DecoderFallbackException || ex is FormatException) { throw new InvalidDataException("Malformed refusal notice.", ex); } } } public sealed class PeerJoinClient { public PeerClassVerdict? Verdict { get; } public byte[]? Answer { get; } public string? Problem { get; } internal PeerJoinClient(PeerClassVerdict? verdict, byte[]? answer, string? problem) { Verdict = verdict; Answer = answer; Problem = problem; } } public sealed class PeerJoinServer { public bool Admit => Notice == null; public PeerClassVerdict? Verdict { get; } public IReadOnlyList<PeerProduct>? ClientProducts { get; } public RefusalNotice? Notice { get; } public string? Problem { get; } internal PeerJoinServer(PeerClassVerdict? verdict, RefusalNotice? notice, string? problem, IReadOnlyList<PeerProduct>? clientProducts = null) { Verdict = verdict; Notice = notice; Problem = problem; ClientProducts = clientProducts; } } public static class PeerJoin { public const string UnreadableAnswer = "Crucible could not read this client's product list; update Crucible on both sides."; public static PeerJoinClient Client(IEnumerable<PeerProduct> local, byte[] greeting, bool handshakeOn) { if (local == null) { throw new ArgumentNullException("local"); } CapabilitySet capabilitySet; try { capabilitySet = CapabilityCodec.Decode(greeting ?? throw new ArgumentNullException("greeting")); } catch (NotSupportedException ex) { return new PeerJoinClient(null, null, ex.Message); } catch (InvalidDataException ex2) { return new PeerJoinClient(null, null, "The server's product list could not be read: " + ex2.Message); } List<PeerProduct> list = local.ToList(); return new PeerJoinClient(PeerClassPolicy.Evaluate(capabilitySet.Products, list), handshakeOn ? CapabilityCodec.Encode(list) : null, null); } public static PeerJoinServer Server(IEnumerable<PeerProduct> local, byte[] answer, Func<string> newCode) { if (local == null) { throw new ArgumentNullException("local"); } if (answer == null) { throw new ArgumentNullException("answer"); } if (newCode == null) { throw new ArgumentNullException("newCode"); } CapabilitySet capabilitySet; try { capabilitySet = CapabilityCodec.Decode(answer); } catch (NotSupportedException ex) { return new PeerJoinServer(null, null, "Client answered with a newer product list format; versions unverified (" + ex.Message + ")"); } catch (InvalidDataException) { return new PeerJoinServer(null, new RefusalNotice(newCode(), new string[1] { "Crucible could not read this client's product list; update Crucible on both sides." }), null); } if (capabilitySet.FormatVersion < 2) { return new PeerJoinServer(null, null, null); } PeerClassVerdict peerClassVerdict = PeerClassPolicy.Evaluate(local, capabilitySet.Products); if (!peerClassVerdict.Admit) { return new PeerJoinServer(peerClassVerdict, RefusalNotice.From(peerClassVerdict, newCode()), null); } return new PeerJoinServer(peerClassVerdict, null, null, capabilitySet.Products); } } public enum AdmissionMode { Strict, AllowVanilla } public enum VanillaSupport { ClientRequired, Mixed, ServerOnlyCapable } public enum PeerKind { Unknown, Pending, Crucible, Vanilla } public enum SilentPeerAction { Refuse, AdmitAsVanilla } public enum HandshakePeerAction { Continue, RefuseBeforeSpawn } public sealed class AdmissionProduct { public string Id { get; } public string Name { get; } public MultiplayerPolicy Policy { get; } public VanillaSupport? Declared { get; } public AdmissionProduct(string id, string name, MultiplayerPolicy policy, VanillaSupport? declared) { Validation.Id(id); if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Invalid product name.", "name"); } if (!Enum.IsDefined(typeof(MultiplayerPolicy), policy)) { throw new ArgumentException("Unknown multiplayer policy.", "policy"); } if (declared.HasValue && !Enum.IsDefined(typeof(VanillaSupport), declared.Value)) { throw new ArgumentException("Unknown vanilla support.", "declared"); } Id = id; Name = name; Policy = policy; Declared = declared; } } public sealed class AdmissionDecision { public AdmissionMode Requested { get; } public AdmissionMode Effective { get; } public IReadOnlyList<string> Reasons { get; } public bool AdmitsVanilla => Effective == AdmissionMode.AllowVanilla; public string Summary { get { if (Requested != AdmissionMode.Strict) { return "Network.AdmissionMode AllowVanilla is no longer supported; Strict applies and every client must run Meadworks. The saved value is retained."; } return "Admission mode Strict: every client must run Meadworks."; } } internal AdmissionDecision(AdmissionMode requested, AdmissionMode effective, IReadOnlyList<string> reasons) { Requested = requested; Effective = effective; Reasons = reasons; } } public static class AdmissionPolicy { public const string VanillaSupportCapability = "meadworks.admission.vanillasupport"; public const string VanillaCountCapability = "meadworks.peers.vanillacount"; public const string PeerKindCapability = "meadworks.peers.kind"; public static readonly Version CapabilityVersion = new Version(1, 0); public const AdmissionMode DefaultMode = AdmissionMode.Strict; public const string RetiredModeNotice = "Network.AdmissionMode AllowVanilla is no longer supported; Strict applies and every client must run Meadworks. The saved value is retained."; public const string VanillaRefusalNotice = "This server requires the Meadworks mod pack. Install it to join. / Este servidor requiere el paquete de mods Meadworks. Instálalo para unirte. / Ce serveur nécessite le pack de mods Meadworks. Installez-le pour rejoindre la partie. / Dieser Server erfordert die Meadworks-Mods. Installiere sie, um beizutreten. / Для этого сервера нужен набор модов Meadworks. Установите его, чтобы присоединиться. / 此服务器需要 Meadworks 模组包。请安装后再加入。 / 이 서버는 Meadworks 모드 팩이 필요합니다. 참가하려면 설치하세요."; public const string VanillaWelcomeNotice = "This server runs Meadworks in server-only mode. Install the mod pack for the full experience. / Este servidor usa Meadworks solo en el servidor. Instala el paquete de mods para la experiencia completa. / Ce serveur utilise Meadworks uniquement sur le serveur. Installez le pack de mods pour l'expérience complète. / Dieser Server führt Meadworks nur auf dem Server aus. Installiere die Mods für das volle Spiel. / На этом сервере Meadworks работает только на сервере. Установите моды, чтобы получить все возможности. / 此服务器仅在服务器端运行 Meadworks。安装模组包即可获得完整体验。 / 이 서버는 서버에서만 Meadworks를 실행합니다. 전체 내용을 보려면 모드 팩을 설치하세요."; public static VanillaSupport SupportOf(AdmissionProduct product) { if (product == null) { throw new ArgumentNullException("product"); } if (product.Declared.HasValue) { return product.Declared.Value; } return product.Policy switch { MultiplayerPolicy.ServerOnly => VanillaSupport.ServerOnlyCapable, MultiplayerPolicy.RequiredOnAllPeers => VanillaSupport.ClientRequired, _ => VanillaSupport.Mixed, }; } public static AdmissionDecision Decide(AdmissionMode requested, IEnumerable<AdmissionProduct> products, IEnumerable<string> jotunnBlockers) { if (!Enum.IsDefined(typeof(AdmissionMode), requested)) { throw new ArgumentException("Unknown admission mode.", "requested"); } List<AdmissionProduct> list = (products ?? throw new ArgumentNullException("products")).ToList(); if (list.Any((AdmissionProduct p) => p == null)) { throw new ArgumentException("Null product.", "products"); } if (list.Select((AdmissionProduct p) => p.Id).Distinct<string>(StringComparer.Ordinal).Count() != list.Count) { throw new ArgumentException("Duplicate product id.", "products"); } if (jotunnBlockers == null) { throw new ArgumentNullException("jotunnBlockers"); } if (requested == AdmissionMode.Strict) { return new AdmissionDecision(requested, AdmissionMode.Strict, Array.Empty<string>()); } return new AdmissionDecision(requested, AdmissionMode.Strict, Array.AsReadOnly(new string[1] { "Network.AdmissionMode AllowVanilla is no longer supported; Strict applies and every client must run Meadworks. The saved value is retained." })); } public static SilentPeerAction ForSilentPeer(AdmissionDecision decision) { if (!(decision ?? throw new ArgumentNullException("decision")).AdmitsVanilla) { return SilentPeerAction.Refuse; } return SilentPeerAction.AdmitAsVanilla; } public static HandshakePeerAction ForPeerInfo(AdmissionDecision decision, bool sentModList) { if (!((decision ?? throw new ArgumentNullException("decision")).AdmitsVanilla || sentModList)) { return HandshakePeerAction.RefuseBeforeSpawn; } return HandshakePeerAction.Continue; } public static IReadOnlyList<AdmissionProduct> Products(ProductRegistry registry) { if (registry == null) { throw new ArgumentNullException("registry"); } Dictionary<string, VanillaSupport> declared = new Dictionary<string, VanillaSupport>(StringComparer.Ordinal); foreach (KeyValuePair<string, Func<int>> item in registry.Capabilities.ResolveEach<Func<int>>("meadworks.admission.vanillasupport", CapabilityVersion)) { int num; try { num = item.Value(); } catch { continue; } if (Enum.IsDefined(typeof(VanillaSupport), num)) { declared[item.Key] = (VanillaSupport)num; } } VanillaSupport value; return (from m in registry.Snapshot() select new AdmissionProduct(m.Id, m.Name, m.Multiplayer, declared.TryGetValue(m.Id, out value) ? new VanillaSupport?(value) : ((VanillaSupport?)null))).ToList().AsReadOnly(); } } public sealed class PeerPresenceTracker { private sealed class Entry { internal PeerKind Kind; internal double Due; internal double Greeted; internal bool SlowNoted; internal int LastQueued = -1; } public const double DefaultCeilingSeconds = 300.0; public const int SendBacklogBytes = 65536; private readonly Dictionary<long, Entry> peers = new Dictionary<long, Entry>(); public double AnswerTimeoutSeconds { get; } public double CeilingSeconds { get; } public int VanillaCount => peers.Values.Count((Entry e) => e.Kind == PeerKind.Vanilla); public int PendingCount => peers.Values.Count((Entry e) => e.Kind == PeerKind.Pending); public bool AnyVanilla => VanillaCount > 0; public PeerPresenceTracker(double answerTimeoutSeconds, double ceilingSeconds = 300.0) { if (double.IsNaN(answerTimeoutSeconds) || double.IsInfinity(answerTimeoutSeconds) || answerTimeoutSeconds <= 0.0) { throw new ArgumentException("Expected a positive finite timeout.", "answerTimeoutSeconds"); } if (double.IsNaN(ceilingSeconds) || double.IsInfinity(ceilingSeconds) || ceilingSeconds <= 0.0) { throw new ArgumentException("Expected a positive finite ceiling.", "ceilingSeconds"); } AnswerTimeoutSeconds = answerTimeoutSeconds; CeilingSeconds = ceilingSeconds; } public void Greeted(long peerId, double now) { if (!peers.ContainsKey(peerId)) { peers[peerId] = new Entry { Kind = PeerKind.Pending, Greeted = now, Due = now + AnswerTimeoutSeconds }; } } public bool Extend(long peerId, double now) { if (!peers.TryGetValue(peerId, out Entry value) || value.Kind != PeerKind.Pending || now >= value.Greeted + CeilingSeconds) { return false; } double num = Math.Min(value.Greeted + CeilingSeconds, Math.Max(value.Due, now + AnswerTimeoutSeconds)); if (num == value.Due) { return false; } value.Due = num; return true; } public bool ObserveQueue(long peerId, double now, int queuedBytes) { if (queuedBytes < 0 || !peers.TryGetValue(peerId, out Entry value) || value.Kind != PeerKind.Pending) { return false; } int lastQueued = value.LastQueued; value.LastQueued = queuedBytes; if (now >= value.Due || now >= value.Greeted + CeilingSeconds || lastQueued <= 65536 || queuedBytes >= lastQueued) { return false; } return Extend(peerId, now); } public bool NoteSlow(long peerId, double now, double formerTimeoutSeconds) { if (!peers.TryGetValue(peerId, out Entry value) || value.Kind != PeerKind.Pending || value.SlowNoted || !(now - value.Greeted > formerTimeoutSeconds)) { return false; } value.SlowNoted = true; return true; } public double Waited(long peerId, double now) { if (!peers.TryGetValue(peerId, out Entry value)) { return AnswerTimeoutSeconds; } return Math.Max(0.0, now - value.Greeted); } public bool Answered(long peerId) { if (!peers.TryGetValue(peerId, out Entry value)) { peers[peerId] = new Entry { Kind = PeerKind.Crucible }; return true; } if (value.Kind != PeerKind.Pending) { return false; } value.Kind = PeerKind.Crucible; return true; } public IReadOnlyList<long> Expire(double now) { List<long> list = (from p in peers where p.Value.Kind == PeerKind.Pending && (now >= p.Value.Due || now >= p.Value.Greeted + CeilingSeconds) select p.Key into i orderby i select i).ToList(); foreach (long item in list) { peers[item].Kind = PeerKind.Vanilla; } return list.AsReadOnly(); } public void Left(long peerId) { peers.Remove(peerId); } public void Retain(IEnumerable<long> connected) { HashSet<long> keep = new HashSet<long>(connected ?? throw new ArgumentNullException("connected")); foreach (long item in peers.Keys.Where((long k) => !keep.Contains(k)).ToList()) { peers.Remove(item); } } public void Reset() { peers.Clear(); } public PeerKind KindOf(long peerId) { if (!peers.TryGetValue(peerId, out Entry value)) { return PeerKind.Unknown; } return value.Kind; } public int NotCrucibleCount(IEnumerable<long> connected) { return (connected ?? throw new ArgumentNullException("connected")).Distinct().Count((long id) => KindOf(id) != PeerKind.Crucible); } public string Describe() { return string.Format(CultureInfo.InvariantCulture, "{0} Crucible, {1} vanilla, {2} pending", peers.Values.Count((Entry e) => e.Kind == PeerKind.Crucible), VanillaCount, PendingCount); } } public sealed class BuildEntry { public string Assembly { get; } public string Version { get; } public string Sha256 { get; } public BuildEntry(string assembly, string version, string sha256) { if (string.IsNullOrWhiteSpace(assembly) || assembly.Length > 128) { throw new ArgumentException("Invalid assembly name.", "assembly"); } if (sha256 == null || sha256.Length != 64 || !sha256.All(Uri.IsHexDigit)) { throw new ArgumentException("SHA256 must be 64 hex characters.", "sha256"); } Assembly = assembly; Version = (((version ?? "").Length > 64) ? version.Substring(0, 64) : (version ?? "")); Sha256 = sha256.ToUpperInvariant(); } } public sealed class BuildComparison { public const string Capability = "meadworks.crucible.build_hashes"; public IReadOnlyList<string> Same { get; } public IReadOnlyList<string> Different { get; } public IReadOnlyList<string> OnlyLocal { get; } public IReadOnlyList<string> OnlyRemote { get; } public bool Matches { get { if (Different.Count == 0 && OnlyLocal.Count == 0) { return OnlyRemote.Count == 0; } return false; } } private BuildComparison(IReadOnlyList<string> same, IReadOnlyList<string> different, IReadOnlyList<string> onlyLocal, IReadOnlyList<string> onlyRemote) { Same = same; Different = different; OnlyLocal = onlyLocal; OnlyRemote = onlyRemote; } public static BuildComparison Compare(IEnumerable<BuildEntry> local, IEnumerable<BuildEntry> remote) { Dictionary<string, BuildEntry> dictionary = Index(local); Dictionary<string, BuildEntry> dictionary2 = Index(remote); List<string> list = dictionary.Keys.Union<string>(dictionary2.Keys, StringComparer.Ordinal).OrderBy<string, string>((string n) => n, StringComparer.Ordinal).ToList(); List<string> list2 = new List<string>(); List<string> list3 = new List<string>(); List<string> list4 = new List<string>(); List<string> list5 = new List<string>(); foreach (string item in list) { BuildEntry value; bool flag = dictionary.TryGetValue(item, out value); BuildEntry value2; bool flag2 = dictionary2.TryGetValue(item, out value2); if (flag && !flag2) { list4.Add(item); continue; } if (!flag) { list5.Add(item); continue; } if (value.Sha256 == value2.Sha256) { list2.Add(item); continue; } list3.Add(item + " " + value.Version + "/" + value2.Version); } return new BuildComparison(list2, list3, list4, list5); } private static Dictionary<string, BuildEntry> Index(IEnumerable<BuildEntry> entries) { Dictionary<string, BuildEntry> dictionary = new Dictionary<string, BuildEntry>(StringComparer.Ordinal); foreach (BuildEntry item in entries ?? Enumerable.Empty<BuildEntry>()) { if (item != null && !dictionary.ContainsKey(item.Assembly)) { dictionary.Add(item.Assembly, item); } } return dictionary; } public string Summary() { if (!Matches) { return "builds differ: " + string.Join("; ", new string[3] { (Different.Count > 0) ? ("different " + string.Join(", ", Different)) : null, (OnlyLocal.Count > 0) ? ("only here " + string.Join(", ", OnlyLocal)) : null, (OnlyRemote.Count > 0) ? ("only there " + string.Join(", ", OnlyRemote)) : null }.Where((string s) => s != null)) + " (" + Same.Count + " same)"; } return "builds match (" + Same.Count + " assemblies)"; } } public sealed class CapabilityLink<T> where T : class { private readonly string id; private readonly Version minimum; private CapabilityRegistry? seen; private int generation = -1; private T? service; public string Id => id; public CapabilityLink(string id, Version minimumVersion) { Validation.Id(id); Validation.Version(minimumVersion); this.id = id; minimum = minimumVersion; } public bool TryGet(CapabilityRegistry? registry, out T? provider) { if (registry == null) { provider = null; return false; } if (registry != seen || registry.Generation != generation) { seen = registry; generation = registry.Generation; if (!registry.TryResolve<T>(id, minimum, out service)) { service = null; } } provider = service; return provider != null; } } public sealed class FeatureLink { private readonly CapabilityLink<Func<bool>> link; public string Id => link.Id; public FeatureLink(string capabilityId) { link = new CapabilityLink<Func<bool>>(capabilityId, new Version(1, 0)); } public bool TryOwned(CapabilityRegistry? registry, out bool on) { on = false; if (!link.TryGet(registry, out Func<bool> provider)) { return false; } try { on = provider(); return true; } catch { on = false; return false; } } public bool On(CapabilityRegistry? registry) { bool on; return TryOwned(registry, out on) && on; } } public sealed class CapabilityMetadata { public string Id { get; } public Version Version { get; } public string Owner { get; } internal CapabilityMetadata(string id, Version version, string owner) { Id = id; Version = version; Owner = owner; } } public sealed class CapabilityRegistry { private sealed class Entry { internal readonly ProductRegistration Owner; internal readonly CapabilityMetadata Metadata; internal readonly object Service; internal readonly Type Contract; internal Entry(ProductRegistration owner, string id, Version version, object service, Type contract) { Owner = owner; Metadata = new CapabilityMetadata(id, version, owner.Metadata.Id); Service = service; Contract = contract; } } private readonly List<Entry> entries = new List<Entry>(); public int Generation { get; private set; } internal CapabilityRegistry(ProductRegistry registry) { } internal void Add<T>(ProductRegistration owner, string id, Version version, T service) where T : class { owner.EnsureActive(); Validation.Id(id); Validation.Version(version); if (!id.StartsWith("meadworks.", StringComparison.Ordinal) && !id.StartsWith(owner.Metadata.Id + ".", StringComparison.Ordinal)) { throw new ArgumentException("Capabilities use the meadworks namespace or the owning product id as prefix."); } if (service == null) { throw new ArgumentNullException("service"); } if (entries.Any((Entry e) => e.Owner == owner && e.Metadata.Id == id)) { throw new InvalidOperationException("Owner already provides this capability."); } entries.Add(new Entry(owner, id, version, service, typeof(T))); Generation++; } public bool TryResolve<T>(string id, Version minimumVersion, out T? service) where T : class { service = null; if (!Validation.IsId(id) || minimumVersion == null || minimumVersion.Major < 0 || minimumVersion.Minor < 0) { return false; } Entry entry = null; for (int i = 0; i < entries.Count; i++) { Entry entry2 = entries[i]; if (!(entry2.Metadata.Id != id) && entry2.Metadata.Version.Major == minimumVersion.Major && !(entry2.Metadata.Version < minimumVersion) && !(entry2.Contract != typeof(T))) { if (entry != null) { return false; } entry = entry2; } } if (entry == null) { return false; } service = (T)entry.Service; return true; } public IReadOnlyList<KeyValuePair<string, T>> ResolveEach<T>(string id, Version minimumVersion) where T : class { Validation.Id(id); Validation.Version(minimumVersion); return Array.AsReadOnly((from e in entries.Where((Entry e) => e.Metadata.Id == id && e.Metadata.Version.Major == minimumVersion.Major && e.Metadata.Version >= minimumVersion && e.Contract == typeof(T)).OrderBy<Entry, string>((Entry e) => e.Metadata.Owner, StringComparer.Ordinal) select new KeyValuePair<string, T>(e.Metadata.Owner, (T)e.Service)).ToArray()); } public IReadOnlyList<CapabilityMetadata> Providers(string id) { Validation.Id(id); return Array.AsReadOnly((from e in entries.Where((Entry e) => e.Metadata.Id == id).OrderBy<Entry, string>((Entry e) => e.Metadata.Owner, StringComparer.Ordinal) select e.Metadata).ToArray()); } internal IReadOnlyList<CapabilityMetadata> All() { return Array.AsReadOnly((from e in entries.OrderBy<Entry, string>((Entry e) => e.Metadata.Id, StringComparer.Ordinal).ThenBy<Entry, string>((Entry e) => e.Metadata.Owner, StringComparer.Ordinal) select e.Metadata).ToArray()); } public bool Any(string id) { if (id == null) { return false; } for (int i = 0; i < entries.Count; i++) { if (entries[i].Metadata.Id == id) { return true; } } return false; } internal void Remove(ProductRegistration owner) { if (entries.RemoveAll((Entry e) => e.Owner == owner) > 0) { Generation++; } } } public enum ConfigScope { Local, ServerAuthoritative, Cheat } public static class ConfigAuthority { public static bool CanEdit(ConfigScope scope, SessionSnapshot session) { if (session != null) { return CanEdit(scope, session.AuthorityReady, session.IsAdmin); } return false; } public static bool CanEdit(ConfigScope scope, bool authorityAvailable, bool isAdmin) { if (scope != ConfigScope.Local) { return scope == ConfigScope.ServerAuthoritative && authorityAvailable && isAdmin; } return true; } } public sealed class MigrationStep { public int From { get; } public int To { get; } public Action Apply { get; } public MigrationStep(int from, int to, Action apply) { if (from < 0 || to != from + 1) { throw new ArgumentException("Migration must advance one schema."); } From = from; To = to; Apply = apply ?? throw new ArgumentNullException("apply"); } } public static class ConfigMigration { public static void Run(int current, int target, IReadOnlyList<MigrationStep> steps, Action backup, Action<int> persist) { if (current < 0 || target < 0) { throw new ArgumentOutOfRangeException("current"); } if (current > target) { throw new NotSupportedException("Future configuration schema; file left unchanged."); } List<MigrationStep> list = new List<MigrationStep>(); int version; for (version = current; version < target; version++) { MigrationStep[] array = steps.Where((MigrationStep s) => s.From == version && s.To == version + 1).ToArray(); if (array.Length != 1) { throw new InvalidOperationException("Missing or ambiguous migration step."); } list.Add(array[0]); } if (list.Count == 0) { return; } backup(); foreach (MigrationStep item in list) { item.Apply(); } persist(target); } } public static class BlockedOptionText { public const string Product = "crucible"; public const string OptionKey = "blocked_option"; private static readonly ConfigBlockReason[] Order = new ConfigBlockReason[7] { ConfigBlockReason.AuthorityPending, ConfigBlockReason.NotAdmin, ConfigBlockReason.ServerLocked, ConfigBlockReason.VanillaServer, ConfigBlockReason.ProductDisabled, ConfigBlockReason.ServerOnly, ConfigBlockReason.MischiefDenied }; private static readonly string[] Keys = new string[7] { "blocked_authority_pending", "blocked_not_admin", "blocked_server_locked", "blocked_vanilla_server", "blocked_product_disabled", "blocked_server_only", "blocked_mischief_denied" }; private static readonly Dictionary<string, string[]> Rows = new Dictionary<string, string[]>(StringComparer.Ordinal) { ["English"] = new string[8] { "Waiting for the server to confirm Meadworks settings.", "Only a server admin can change this.", "The server controls this setting.", "This server does not run Meadworks, so this is locked.", "This server does not run this product, so it is turned off for this session.", "This product runs only on the server.", "The server's cheat policy does not allow you to use this cheat.", "{0}: {1}" }, ["Korean"] = new string[8] { "서버가 Meadworks 설정을 확인하기를 기다리는 중입니다.", "서버 관리자만 이것을 변경할 수 있습니다.", "이 설정은 서버가 관리합니다.", "이 서버는 Meadworks를 실행하지 않으므로 잠겨 있습니다.", "이 서버는 이 제품을 실행하지 않으므로 이번 세션 동안 꺼집니다.", "이 제품은 서버에서만 실행됩니다.", "서버의 치트 정책상 이 치트를 사용할 수 없습니다.", "{0}: {1}" }, ["Spanish"] = new string[8] { "Esperando a que el servidor confirme los ajustes de Meadworks.", "Solo un administrador del servidor puede cambiar esto.", "El servidor controla este ajuste.", "Este servidor no ejecuta Meadworks, así que esto está bloqueado.", "Este servidor no ejecuta este producto, así que está desactivado durante esta sesión.", "Este producto solo se ejecuta en el servidor.", "La política de trucos del servidor no te permite usar este truco.", "{0}: {1}" }, ["Chinese"] = new string[8] { "正在等待服务器确认 Meadworks 设置。", "只有服务器管理员可以更改此项。", "此设置由服务器控制。", "此服务器未运行 Meadworks,因此此项已锁定。", "此服务器未运行该产品,因此本次会话中已关闭。", "该产品仅在服务器上运行。", "服务器的作弊策略不允许你使用此作弊。", "{0}:{1}" }, ["Russian"] = new string[8] { "Ожидание подтверждения настроек Meadworks сервером.", "Изменить это может только администратор сервера.", "Этот параметр контролирует сервер.", "На этом сервере нет Meadworks, поэтому это заблокировано.", "На этом сервере нет этого продукта, поэтому он отключён на этот сеанс.", "Этот продукт работает только на сервере.", "Политика читов на сервере не разрешает вам использовать этот чит.", "{0}: {1}" }, ["French"] = new string[8] { "En attente de la confirmation des paramètres Meadworks par le serveur.", "Seul un administrateur du serveur peut modifier ceci.", "Le serveur contrôle ce paramètre.", "Ce serveur n'utilise pas Meadworks, c'est donc verrouillé.", "Ce serveur n'utilise pas ce produit, il est donc désactivé pour cette session.", "Ce produit ne fonctionne que sur le serveur.", "La politique de triche du serveur ne vous permet pas d'utiliser cette triche.", "{0} : {1}" }, ["German"] = new string[8] { "Warte darauf, dass der Server die Meadworks-Einstellungen bestätigt.", "Nur ein Serveradmin kann das ändern.", "Der Server legt diese Einstellung fest.", "Dieser Server nutzt Meadworks nicht, daher ist das gesperrt.", "Dieser Server nutzt dieses Produkt nicht, daher ist es für diese Sitzung ausgeschaltet.", "Dieses Produkt läuft nur auf dem Server.", "Die Cheat-Richtlinie des Servers erlaubt dir diesen Cheat nicht.", "{0}: {1}" } }; private static readonly Dictionary<string, Dictionary<string, string>> SettingRows = new Dictionary<string, Dictionary<string, string>>(StringComparer.Ordinal) { ["English"] = new Dictionary<string, string>(StringComparer.Ordinal) { ["cfg_achievements_heading"] = "Achievements", ["cfg_achievements__syncpaststats_name"] = "SyncPastStats", ["cfg_achievements__syncpaststats_help"] = "Copy stats the game counted while achievements were not tracked into the achievement table when a clean character loads (never for characters that used cheats unless Hex allows it). Off while AchievementSync is installed.", ["cfg_achievements__resynckey_name"] = "ResyncKey", ["cfg_achievements__resynckey_help"] = "Key that rechecks your saved game statistics and grants any MeadWorks achievements already earned. Unassigned by default.", ["cfg_authority_heading"] = "Authority", ["cfg_authority__vanillaservertimeoutseconds_name"] = "VanillaServerTimeoutSeconds", ["cfg_authority__vanillaservertimeoutseconds_help"] = "Seconds a client waits for the server's Crucible Hello before treating the server as vanilla (cheats locked, required products off).", ["cfg_network_heading"] = "Network", ["cfg_network__peerclasshandshake_name"] = "PeerClassHandshake", ["cfg_network__peerclasshandshake_help"] = "Checks MeadWorks versions when a player joins: the server turns away clients missing a required MeadWorks mod or running a different major.minor version, and a client turns off required mods the server lacks. On by default; turn it off only to diagnose join problems. Restart the game after changing this setting.", ["cfg_logging_heading"] = "Logging", ["cfg_logging__level_name"] = "Level", ["cfg_logging__level_help"] = "Meadworks log level: Quiet (errors and warnings), Normal, Verbose or Debug. Also: console 'meadworks log <level> [product]'.", ["cfg_logging__productoverrides_name"] = "ProductOverrides", ["cfg_logging__productoverrides_help"] = "Per-product levels, for example 'Wildroot=Debug,Burrow=Normal'.", ["cfg_logging__repeatwindowseconds_name"] = "RepeatWindowSeconds", ["cfg_logging__repeatwindowseconds_help"] = "Identical lines inside this window are written once and counted; 0 writes every line.", ["cfg_logging__consolefilterenabled_name"] = "ConsoleFilterEnabled", ["cfg_logging__consolefilterenabled_help"] = "Filter the BepInEx console for all installed mods by the rules below.", ["cfg_logging__consolefilterrules_name"] = "ConsoleFilterRules", ["cfg_logging__consolefilterrules_help"] = "Per-source console levels: 'Source=All|Info|Warning|Error|None', separated by ';'. '*' sets the level for unlisted sources.", ["cfg_logging__consolefilterappliestodisklog_name"] = "ConsoleFilterAppliesToDiskLog", ["cfg_logging__consolefilterappliestodisklog_help"] = "Also apply the console filter rules to BepInEx's LogOutput.log.", ["cfg_logging__showallconsolemessages_name"] = "ShowAllConsoleMessages", ["cfg_logging__showallconsolemessages_help"] = "Show every message on the BepInEx console. By default Crucible hides a short, exact list of known-harmless vanilla and Unity messages (world-generation hints, missing audio or video shaders on a dedicated server and similar); LogOutput.log always keeps everything.", ["cfg_mischief_heading"] = "Mischief", ["cfg_mischief__listedplayers_name"] = "ListedPlayers", ["cfg_mischief__listedplayers_help"] = "Platform user IDs granted Listed cheat categories, separated by commas (for example Steam_76561198000000000; a bare number means a Steam ID, as in adminlist.txt).", ["cfg_skills_heading"] = "Skills", ["cfg_skills__experiencemultiplier_name"] = "ExperienceMultiplier", ["cfg_skills__experiencemultiplier_help"] = "Multiplies experience in every Meadworks custom skill (products' own skills, not the game's). The server's value applies in multiplayer.", ["cfg_mischief__personal_name"] = "Personal", ["cfg_mischief__personal_help"] = "Who may use Personal cheats on this server: Nobody, Admins, Listed (admins and Listed Players) or Everyone. Single player and the host always may. Personal: affects only the user (god mode, speed, size).", ["cfg_mischief__economy_name"] = "Economy", ["cfg_mischief__economy_help"] = "Who may use Economy cheats on this server: Nobody, Admins, Listed (admins and Listed Players) or Everyone. Single player and the host always may. Economy: creates or saves materials (spawning, craft speed, loot multipliers, free build).", ["cfg_mischief__world_name"] = "World", ["cfg_mischief__world_help"] = "Who may use World cheats on this server: Nobody, Admins, Listed (admins and Listed Players) or Everyone. Single player and the host always may. World: changes the shared world (weather, unrestricted growth).", ["cfg_mischief__information_name"] = "Information", ["cfg_mischief__information_help"] = "Who may use Information cheats on this server: Nobody, Admins, Listed (admins and Listed Players) or Everyone. Single player and the host always may. Information: reveals what is hidden (ESP, map reveal, points of interest).", ["cfg_mischief__travel_name"] = "Travel", ["cfg_mischief__travel_help"] = "Who may use Travel cheats on this server: Nobody, Admins, Listed (admins and Listed Players) or Everyone. Single player and the host always may. Travel: moves players (teleport, portals).", ["cfg_mischief__progress_name"] = "Progress", ["cfg_mischief__progress_help"] = "Who may use Progress cheats on this server: Nobody, Admins, Listed (admins and Listed Players) or Everyone. Single player and the host always may. Progress: skills, boss powers, achievements, guaranteed upgrades." }, ["Spanish"] = new Dictionary<string, string>(StringComparer.Ordinal) { ["cfg_achievements_heading"] = "Logros", ["cfg_achievements__syncpaststats_name"] = "Sincronizar estadísticas pasadas", ["cfg_achievements__syncpaststats_help"] = "Copia a la tabla de logros las estadísticas que el juego contó mientras no se registraban logros, al cargar un personaje limpio (nunca en personajes que usaron trucos, salvo que Hex lo permita). Desactivado si AchievementSync está instalado.", ["cfg_achievements__resynckey_name"] = "Tecla de resincronización", ["cfg_achievements__resynckey_help"] = "Tecla que vuelve a revisar las estadísticas de tu partida guardada y concede los logros de MeadWorks ya conseguidos. Sin asignar por defecto.", ["cfg_authority_heading"] = "Autoridad", ["cfg_authority__vanillaservertimeoutseconds_name"] = "Espera de servidor vanilla (segundos)", ["cfg_authority__vanillaservertimeoutseconds_help"] = "Segundos que un cliente espera el saludo (Hello) de Crucible del servidor antes de tratarlo como vanilla (trucos bloqueados, productos obligatorios desactivados).", ["cfg_network_heading"] = "Red", ["cfg_network__peerclasshandshake_name"] = "Comprobación de versiones al unirse", ["cfg_network__peerclasshandshake_help"] = "Comprueba las versiones de MeadWorks cuando un jugador se une: el servidor rechaza a los clientes a los que les falta un mod obligatorio de MeadWorks o que usan otra versión mayor.menor, y un cliente desactiva los mods obligatorios que el servidor no tiene. Activado por defecto; desactívalo solo para diagnosticar problemas al unirse. Reinicia el juego después de cambiar este ajuste.", ["cfg_logging_heading"] = "Registro", ["cfg_logging__level_name"] = "Nivel", ["cfg_logging__level_help"] = "Nivel de registro de Meadworks: Quiet (errores y advertencias), Normal, Verbose o Debug. También: en la consola, 'meadworks log <level> [product]'.", ["cfg_logging__productoverrides_name"] = "Niveles por producto", ["cfg_logging__productoverrides_help"] = "Niveles por producto, por ejemplo 'Wildroot=Debug,Burrow=Normal'.", ["cfg_logging__repeatwindowseconds_name"] = "Ventana de repetición (segundos)", ["cfg_logging__repeatwindowseconds_help"] = "Las líneas idénticas dentro de este intervalo se escriben una vez y se cuentan; 0 escribe todas las líneas.", ["cfg_logging__consolefilterenabled_name"] = "Filtro de consola activado", ["cfg_logging__consolefilterenabled_help"] = "Filtra la consola de BepInEx para todos los mods instalados según las reglas de abajo.", ["cfg_logging__consolefilterrules_name"] = "Reglas del filtro de consola", ["cfg_logging__consolefilterrules_help"] = "Niveles de consola por origen: 'Source=All|Info|Warning|Error|None', separados por ';'. '*' fija el nivel de los orígenes no listados.", ["cfg_logging__consolefilterappliestodisklog_name"] = "Filtro de consola también en el registro en disco", ["cfg_logging__consolefilterappliestodisklog_help"] = "Aplica también las reglas del filtro de consola a LogOutput.log de BepInEx.", ["cfg_logging__showallconsolemessages_name"] = "Mostrar todos los mensajes de consola", ["cfg_logging__showallconsolemessages_help"] = "Muestra todos los mensajes en la consola de BepInEx. Por defecto, Crucible oculta una lista breve y exacta de mensajes inofensivos conocidos del juego base y de Unity (pistas de generación del mundo, shaders de audio o vídeo ausentes en un servidor dedicado y similares); LogOutput.log siempre lo conserva todo.", ["cfg_mischief_heading"] = "Travesuras", ["cfg_mischief__listedplayers_name"] = "Jugadores listados", ["cfg_mischief__listedplayers_help"] = "ID de usuario de plataforma a los que se conceden las categorías de trucos Listed, separados por comas (por ejemplo Steam_76561198000000000; un número sin prefijo es un ID de Steam, como en adminlist.txt).", ["cfg_skills_heading"] = "Habilidades", ["cfg_skills__experiencemultiplier_name"] = "Multiplicador de experiencia", ["cfg_skills__experiencemultiplier_help"] = "Multiplica la experiencia de cada habilidad personalizada de Meadworks (las habilidades propias de los productos, no las del juego). En multijugador se aplica el valor del servidor.", ["cfg_mischief__personal_name"] = "Personales", ["cfg_mischief__personal_help"] = "Quién puede usar trucos Personales en este servidor: Nobody, Admins, Listed (administradores y Jugadores listados) o Everyone. En un jugador y el anfitrión siempre se puede. Personales: solo afectan a quien los usa (modo dios, velocidad, tamaño).", ["cfg_mischief__economy_name"] = "Economía", ["cfg_mischief__economy_help"] = "Quién puede usar trucos de Economía en este servidor: Nobody, Admins, Listed (administradores y Jugadores listados) o Everyone. En un jugador y el anfitrión siempre se puede. Economía: crean o ahorran materiales (aparición de objetos, velocidad de fabricación, multiplicadores de botín, construcción gratis).", ["cfg_mischief__world_name"] = "Mundo", ["cfg_mischief__world_help"] = "Quién puede usar trucos de Mundo en este servidor: Nobody, Admins, Listed (administradores y Jugadores listados) o Everyone. En un jugador y el anfitrión siempre se puede. Mundo: cambian el mundo compartido (clima, crecimiento sin restricciones).", ["cfg_mischief__information_name"] = "Información", ["cfg_mischief__information_help"] = "Quién puede usar trucos de Información en este servidor: Nobody, Admins, Listed (administradores y Jugadores listados) o Everyone. En un jugador y el anfitrión siempre se puede. Información: revelan lo oculto (ESP, revelar el mapa, puntos de interés).", ["cfg_mischief__travel_name"] = "Viaje", ["cfg_mischief__travel_help"] = "Quién puede usar trucos de Viaje en este servidor: Nobody, Admins, Listed (administradores y Jugadores listados) o Everyone. En un jugador y el anfitrión siempre se puede. Viaje: mueven a los jugadores (teletransporte, portales).", ["cfg_mischief__progress_name"] = "Progreso", ["cfg_mischief__progress_help"] = "Quién puede usar trucos de Progreso en este servidor: Nobody, Admins, Listed (administradores y Jugadores listados) o Everyone. En un jugador y el anfitrión siempre se puede. Progreso: habilidades, poderes de jefes, logros, mejoras garantizadas." }, ["French"] = new Dictionary<string, string>(StringComparer.Ordinal) { ["cfg_achievements_heading"] = "Succès", ["cfg_achievements__syncpaststats_name"] = "Synchroniser les statistiques passées", ["cfg_achievements__syncpaststats_help"] = "Copie dans la table des succès les statistiques que le jeu a comptées pendant que les succès n'étaient pas suivis, au chargement d'un personnage propre (jamais pour les personnages ayant triché, sauf si Hex l'autorise). Désactivé si AchievementSync est installé.", ["cfg_achievements__resynckey_name"] = "Touche de resynchronisation", ["cfg_achievements__resynckey_help"] = "Touche qui revérifie les statistiques de votre partie sauvegardée et accorde les succès MeadWorks déjà obtenus. Non attribuée par défaut.", ["cfg_authority_heading"] = "Autorité", ["cfg_authority__vanillaservertimeoutseconds_name"] = "Attente d'un serveur vanilla (secondes)", ["cfg_authority__vanillaservertimeoutseconds_help"] = "Secondes pendant lesquelles un client attend le message Hello de Crucible du serveur avant de le traiter comme vanilla (triches verrouillées, produits requis désactivés).", ["cfg_network_heading"] = "Réseau", ["cfg_network__peerclasshandshake_name"] = "Vérification des versions à la connexion", ["cfg_network__peerclasshandshake_help"] = "Vérifie les versions de MeadWorks quand un joueur se connecte : le serveur refuse les clients auxquels il manque un mod MeadWorks requis ou qui utilisent une autre version majeure.mineure, et un client désactive les mods requis que le serveur n'a pas. Activé par défaut ; ne le désactivez que pour diagnostiquer des problèmes de connexion. Redémarrez le jeu après avoir modifié ce paramètre.", ["cfg_logging_heading"] = "Journalisation", ["cfg_logging__level_name"] = "Niveau", ["cfg_logging__level_help"] = "Niveau de journalisation de Meadworks : Quiet (erreurs et avertissements), Normal, Verbose ou Debug. Aussi : dans la console, 'meadworks log <level> [product]'.", ["cfg_logging__productoverrides_name"] = "Niveaux par produit", ["cfg_logging__productoverrides_help"] = "Niveaux par produit, par exemple 'Wildroot=Debug,Burrow=Normal'.", ["cfg_logging__repeatwindowseconds_name"] = "Fenêtre de répétition (secondes)", ["cfg_logging__repeatwindowseconds_help"] = "Les lignes identiques dans cet intervalle sont écrites une fois et comptées ; 0 écrit chaque ligne.", ["cfg_logging__consolefilterenabled_name"] = "Filtre de console activé", ["cfg_logging__consolefilterenabled_help"] = "Filtre la console BepInEx pour tous les mods installés selon les règles ci-dessous.", ["cfg_logging__consolefilterrules_name"] = "Règles du filtre de console", ["cfg_logging__consolefilterrules_help"] = "Niveaux de console par source : 'Source=All|Info|Warning|Error|None', séparés par ';'. '*' fixe le niveau des sources non listées.", ["cfg_logging__consolefilterappliestodisklog_name"] = "Filtre de console aussi sur le journal disque", ["cfg_logging__consolefilterappliestodisklog_help"] = "Applique aussi les règles du filtre de console au fichier LogOutput.log de BepInEx.", ["cfg_logging__showallconsolemessages_name"] = "Afficher tous les messages de console", ["cfg_logging__showallconsolemessages_help"] = "Affiche chaque message dans la console BepInEx. Par défaut, Crucible masque une liste courte et exacte de messages inoffensifs connus du jeu de base et de Unity (indices de génération du monde, shaders audio ou vidéo absents sur un serveur dédié et similaires) ; LogOutput.log conserve toujours tout.", ["cfg_mischief_heading"] = "Malice", ["cfg_mischief__listedplayers_name"] = "Joueurs listés", ["cfg_mischief__listedplayers_help"] = "Identifiants utilisateur de plateforme auxquels sont accordées les catégories de triche Listed, séparés par des virgules (par exemple Steam_76561198000000000 ; un nombre seul est un identifiant Steam, comme dans adminlist.txt).", ["cfg_skills_heading"] = "Compétences", ["cfg_skills__experiencemultiplier_name"] = "Multiplicateur d'expérience", ["cfg_skills__experiencemultiplier_help"] = "Multiplie l'expérience de chaque compétence personnalisée de Meadworks (les compétences propres aux produits, pas celles du jeu). En multijoueur, la valeur du serveur s'applique.", ["cfg_mischief__personal_name"] = "Personnelles", ["cfg_mischief__personal_help"] = "Qui peut utiliser les triches Personnelles sur ce serveur : Nobody, Admins, Listed (administrateurs et Joueurs listés) ou Everyone. En solo et pour l'hôte, c'est toujours permis. Personnelles : n'affectent que l'utilisateur (mode dieu, vitesse, taille).", ["cfg_mischief__economy_name"] = "Économie", ["cfg_mischief__economy_help"] = "Qui peut utiliser les triches d'Économie sur ce serveur : Nobody, Admins, Listed (administrateurs et Joueurs listés) ou Everyone. En solo et pour l'hôte, c'est toujours permis. Économie : créent ou économisent des matériaux (apparition d'objets, vitesse d'artisanat, multiplicateurs de butin, construction gratuite).", ["cfg_mischief__world_name"] = "Monde", ["cfg_mischief__world_help"] = "Qui peut utiliser les triches de Monde sur ce serveur : Nobody, Admins, Listed (administrateurs et Joueurs listés) ou Everyone. En solo et pour l'hôte, c'est toujours permis. Monde : modifient le monde partagé (météo, croissance sans restriction).", ["cfg_mischief__information_name"] = "Information", ["cfg_mischief__information_help"] = "Qui peut utiliser les triches d'Information sur ce serveur : Nobody, Admins, Listed (administrateurs et Joueurs listés) ou Everyone. En solo et pour l'hôte, c'est toujours permis. Information : révèlent ce qui est caché (ESP, révélation de la carte, points d'intérêt).", ["cfg_mischief__travel_name"] = "Voyage", ["cfg_mischief__travel_help"] = "Qui peut utiliser les triches de Voyage sur ce serveur : Nobody, Admins, Listed (administrateurs et Joueurs listés) ou Everyone. En solo et pour l'hôte, c'est toujours permis. Voyage : déplacent les joueurs (téléportation, portails).", ["cfg_mischief__progress_name"] = "Progression", ["cfg_mischief__progress_help"] = "Qui peut utiliser les triches de Progression sur ce serveur : Nobody, Admins, Listed (admini
plugins/Crucible/Crucible.UI.dll
Decompiled 10 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using ExtraPotions.Meadworks.Contracts; using ExtraPotions.Meadworks.Crucible.UI.PlateColumn; using ExtraPotions.Meadworks.Crucible.UI.WindowInput; using GUIFramework; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExtraPotions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")] [assembly: AssemblyFileVersion("0.3.23.0")] [assembly: AssemblyInformationalVersion("0.3.23+fatal: not a git repository (or any of the parent directories): .git")] [assembly: AssemblyProduct("Crucible.UI")] [assembly: AssemblyTitle("Crucible.UI")] [assembly: AssemblyVersion("0.3.23.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 ExtraPotions.Meadworks.Crucible.UI { [HarmonyPatch(typeof(FejdStartup), "Start")] public static class ArtWarmup { [HarmonyPostfix] public static void Postfix() { if (!KitLook.On || !MeadworksHeadless.AllowsUi) { return; } try { KitArt.Prewarm(); } catch (Exception ex) { MeadworksLog log = CrucibleUiPlugin.Log; if (log != null) { log.Warning("Crucible.UI: could not prepare the panel images early: " + ex.Message); } } } } public static class CrossMark { private const int Size = 64; private static Sprite sprite; public static Sprite Sprite { get { if (!((Object)(object)sprite != (Object)null)) { return sprite = Paint(); } return sprite; } } private static Sprite Paint() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_002c: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false) { name = "Crucible_cross", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)32 }; val.SetPixels32(KitPaintUnity.White(KitPaint.CrossAlpha())); val.Apply(false, true); Sprite obj = Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 100f); ((Object)obj).name = "Crucible_cross"; ((Object)obj).hideFlags = (HideFlags)32; return obj; } } public static class GamePanelTheme { private sealed class FallbackState { private readonly Sprite sprite; private readonly Material material; private readonly Type type; private readonly Color color; private readonly bool fill; private readonly float ppu; private readonly TimberBackground wood; private readonly Texture texture; private readonly Material woodMaterial; private readonly Color woodColor; private readonly bool woodActive; private readonly bool woodEnabled; private readonly bool woodRaycast; private readonly Vector2 size; private readonly Vector2 position; private readonly Vector2 min; private readonly Vector2 max; private readonly Vector2 pivot; private readonly Vector3 scale; internal FallbackState(Image image) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: 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) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) sprite = image.sprite; material = ((Graphic)image).material; type = image.type; color = ((Graphic)image).color; fill = image.fillCenter; ppu = image.pixelsPerUnitMultiplier; Transform val = ((Component)image).transform.Find("Crucible_timberwood"); wood = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TimberBackground>() : null); if (!((Object)(object)wood == (Object)null)) { texture = ((Graphic)wood).mainTexture; woodMaterial = ((Graphic)wood).material; woodColor = ((Graphic)wood).color; woodActive = ((Component)wood).gameObject.activeSelf; woodEnabled = ((Behaviour)wood).enabled; woodRaycast = ((Graphic)wood).raycastTarget; RectTransform rectTransform = ((Graphic)wood).rectTransform; size = rectTransform.sizeDelta; position = rectTransform.anchoredPosition; min = rectTransform.anchorMin; max = rectTransform.anchorMax; pivot = rectTransform.pivot; scale = ((Transform)rectTransform).localScale; } } internal bool Matches(Image image) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: 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_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)image.sprite != (Object)(object)sprite || (Object)(object)((Graphic)image).material != (Object)(object)material || image.type != type || ((Graphic)image).color != color || image.fillCenter != fill || image.pixelsPerUnitMultiplier != ppu) { return false; } Transform val = ((Component)image).transform.Find("Crucible_timberwood"); if ((Object)(object)(((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TimberBackground>() : null) != (Object)(object)wood) { return false; } if ((Object)(object)wood == (Object)null) { return true; } RectTransform rectTransform = ((Graphic)wood).rectTransform; if ((Object)(object)((Graphic)wood).mainTexture == (Object)(object)texture && (Object)(object)((Graphic)wood).material == (Object)(object)woodMaterial && ((Graphic)wood).color == woodColor && ((Component)wood).gameObject.activeSelf == woodActive && ((Behaviour)wood).enabled == woodEnabled && ((Graphic)wood).raycastTarget == woodRaycast && rectTransform.sizeDelta == size && rectTransform.anchoredPosition == position && rectTransform.anchorMin == min && rectTransform.anchorMax == max && rectTransform.pivot == pivot) { return ((Transform)rectTransform).localScale == scale; } return false; } } [HarmonyPatch(typeof(Image), "OnEnable")] private static class PanelEnabled { private static void Postfix(Image __instance) { if (__instance.fillCenter && On && WoodSprite(__instance.sprite) && !KitNativeDialogs.Owns(__instance) && !KitNativeMenus.Owns(__instance) && !KitNativeMenus.Reserves(__instance) && (Object)(object)((Component)__instance).transform.Find("Crucible_timberwood") == (Object)null && !KitSkin.Owns(__instance) && IsPanel(__instance) && KitUiOwnership.Styleable(((Component)__instance).transform)) { newlyEnabled.Add(__instance); } } } private static bool pending = true; private static bool everOn; private static Image[] scan; private static int scanned; private static readonly HashSet<Image> newlyEnabled = new HashSet<Image>(); private static readonly Dictionary<Sprite, bool> woodSprites = new Dictionary<Sprite, bool>(); private static readonly Dictionary<Image, FallbackState> dressed = new Dictionary<Image, FallbackState>(); public static bool On { get { if (KitLook.Timber && KitLook.ThemeGamePanels) { return (Object)(object)KitArt.Panel != (Object)null; } return false; } } public static void Initialize() { SceneManager.sceneLoaded += delegate { woodSprites.Clear(); dressed.Clear(); RequestRefresh(); }; KitLook.Changed += RequestRefresh; } public static void RequestRefresh() { pending = true; } public static void Update() { if (!pending && scan == null && newlyEnabled.Count == 0) { return; } bool flag = On; everOn |= flag; if (pending) { pending = false; if (everOn) { scan = Resources.FindObjectsOfTypeAll<Image>(); scanned = 0; } } if (scan != null) { ScanSome(flag); } foreach (Image item in newlyEnabled) { if ((Object)(object)item != (Object)null && Themed(item)) { Dress(item, flag); } } newlyEnabled.Clear(); } private static void ScanSome(bool on) { int num = KitScan.End(scanned, scan.Length); while (scanned < num) { Image val = scan[scanned]; if ((Object)(object)val != (Object)null && Themed(val)) { Dress(val, on); } scanned++; } if (scanned >= scan.Length) { scan = null; } } private static void Dress(Image image, bool on) { if (on) { TimberBackground.RegisterForged(image); } TimberBackground.Apply(image, on); KitForged.GamePanel(image, on); if (on) { dressed[image] = new FallbackState(image); } else { dressed.Remove(image); } } public static bool TryRelease(Image image) { if ((Object)(object)image == (Object)null || KitSkin.Owns(image) || KitNativeMenus.Owns(image) || KitNativeDialogs.Owns(image)) { return false; } if (dressed.TryGetValue(image, out FallbackState value)) { if (!value.Matches(image) || !KitForged.TryReleaseGamePanel(image)) { return false; } TimberBackground.Apply(image, on: false); dressed.Remove(image); newlyEnabled.Remove(image); return true; } if ((Object)(object)((Component)image).transform.Find("Crucible_timberwood") != (Object)null) { return false; } return KitForged.TryReleaseGamePanel(image); } private static bool Themed(Image image) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) Scene scene = ((Component)image).gameObject.scene; if (((Scene)(ref scene)).IsValid() && (Object)(object)((Graphic)image).canvas != (Object)null && !KitSkin.Owns(image) && !KitNativeDialogs.Owns(image) && !KitNativeMenus.Owns(image) && !KitNativeMenus.Reserves(image) && IsPanel(image)) { return KitUiOwnership.Styleable(((Component)image).transform); } return false; } private static bool IsPanel(Image image) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Invalid comparison between Unknown and I4 bool flag = (Object)(object)((Component)image).transform.Find("Crucible_timberwood") != (Object)null; bool flag2 = (int)image.type == 1 || (int)image.type == 2; if (!flag && (!flag2 || !image.fillCenter || !WoodSprite(image.sprite))) { return false; } return KitThemeRules.IsGamePanel(flag, flag2, image.fillCenter, flag ? null : ((Object)image.sprite).name, ((Object)image).name); } private static bool WoodSprite(Sprite sprite) { if ((Object)(object)sprite == (Object)null) { return false; } if (!woodSprites.TryGetValue(sprite, out var value)) { value = KitThemeRules.IsWoodSprite(((Object)sprite).name); woodSprites[sprite] = value; } return value; } } public static class KitArt { private static readonly Dictionary<string, Sprite> sprites = new Dictionary<string, Sprite>(); public static bool Timber => KitLook.Timber; public static Sprite Panel { get { if (!Timber) { return BrownPanel; } return Wallpaper; } } public static Sprite BrownPanel => Read("panel"); public static Sprite Wallpaper => Read("timber_background") ?? BrownPanel; public static Sprite Cell => Read("cell"); public static Sprite Button { get { if (!Timber) { return BrownButton; } return Read("timber_button") ?? BrownButton; } } public static Sprite TimberButton => Read("timber_button"); public static Sprite BrownButton => Read("button"); public static Sprite ButtonHover => Read("button_hover"); public static Sprite ButtonPressed => Read("button_pressed"); public static Sprite RingPanel => Read("ring_panel"); public static Sprite RingLine => Read("ring_line"); public static Sprite MapFrameLarge => Read("map_frame_large"); public static Sprite Icon(string slot) { return Read("icon_" + slot); } public static bool IsPanel(Sprite sprite) { if ((Object)(object)sprite != (Object)null) { if (!(((Object)sprite).name == KitArtCatalog.SpriteName("panel")) && !(((Object)sprite).name == KitArtCatalog.SpriteName("timber_panel"))) { return TimberFrame.Owns(sprite); } return true; } return false; } public static int Prewarm() { Sprite[] obj = new Sprite[9] { Wallpaper, BrownPanel, Cell, Button, BrownButton, ButtonHover, ButtonPressed, RingPanel, RingLine }; int num = 0; Sprite[] array = (Sprite[])(object)obj; for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null) { num++; } } foreach (string slotIcon in KitArtCatalog.SlotIcons) { if ((Object)(object)Icon(slotIcon) != (Object)null) { num++; } } MeadworksLog log = CrucibleUiPlugin.Log; if (log != null) { log.Info($"Crucible.UI: {num} panel images ready"); } return num; } private static Sprite Read(string name) { if (sprites.TryGetValue(name, out Sprite value)) { return value; } if (!MeadworksHeadless.AllowsUi) { return null; } sprites[name] = null; string text = KitArtCatalog.Resource(name); if (typeof(KitArt).Assembly.GetManifestResourceInfo(text) == null) { return null; } Sprite val = EmbeddedSprite.Load(typeof(KitArt).Assembly, text, KitArtCatalog.SpriteName(name)); if ((Object)(object)val == (Object)null) { MeadworksLog log = CrucibleUiPlugin.Log; if (log != null) { log.Warning("could not read the embedded image " + text + "; the plain brown is used instead"); } return null; } value = Resliced(val.texture, name); Object.Destroy((Object)(object)val); sprites[name] = value; return value; } private static Sprite Resliced(Texture2D texture, string name) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) ((Texture)texture).wrapMode = (TextureWrapMode)1; KitSlice val = KitArtCatalog.Slice(name, ((Texture)texture).width, ((Texture)texture).height); KitRect area = ((KitSlice)(ref val)).Area; float x = ((KitRect)(ref area)).X; area = ((KitSlice)(ref val)).Area; float y = ((KitRect)(ref area)).Y; area = ((KitSlice)(ref val)).Area; float width = ((KitRect)(ref area)).Width; area = ((KitSlice)(ref val)).Area; Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(x, y, width, ((KitRect)(ref area)).Height); float border = ((KitSlice)(ref val)).Border; Sprite obj = Sprite.Create(texture, val2, new Vector2(0.5f, 0.5f), ((KitSlice)(ref val)).PixelsPerUnit, 0u, (SpriteMeshType)0, new Vector4(border, border, border, border)); ((Object)obj).name = KitArtCatalog.SpriteName(name); return obj; } } public static class KitSkin { public const float GameOverhang = 10f; public const float WoodOverhang = 12f; private static readonly Dictionary<Image, Sprite> panelSprites = new Dictionary<Image, Sprite>(); private static readonly Dictionary<Image, Material> panelMaterials = new Dictionary<Image, Material>(); private static readonly HashSet<Image> owned = new HashSet<Image>(); private static readonly List<Image> gone = new List<Image>(); private static Sprite gameCell; private static bool hooked; public static bool On => KitLook.On; public static float TopPad { get { if (!On) { return 0f; } return 4f; } } public static float PanelOverhang => OverhangFor(On); public static float OverhangFor(bool on) { if (!on || !((Object)(object)KitArt.Panel != (Object)null)) { return 10f; } return 12f; } public static bool Owns(Image image) { if ((Object)(object)image != (Object)null) { return owned.Contains(image); } return false; } public static void Background(Image image) { Background(image, On); } public static void Background(Image image, bool on) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) Panel(image, on); if ((Object)(object)image != (Object)null) { ((Graphic)image).rectTransform.sizeDelta = Vector2.one * (OverhangFor(on) * 2f); } } public static void Panel(Image image) { Panel(image, On); } public static void GamePanel(Image image, bool on) { if (!((Object)(object)image == (Object)null)) { bool flag = on && KitArt.Timber && (Object)(object)KitArt.Panel != (Object)null; if (flag) { TimberBackground.RegisterForged(image); } Panel(image, on); KitForged.GamePanel(image, flag); } } public static void GameBackground(Image image, bool on) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) GamePanel(image, on); if ((Object)(object)image != (Object)null) { ((Graphic)image).rectTransform.sizeDelta = Vector2.one * (OverhangFor(on) * 2f); } } public static void Panel(Image image, bool on) { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)image == (Object)null)) { Hook(); owned.Add(image); TimberBackground.Apply(image, on: false); if (!panelSprites.ContainsKey(image) && (Object)(object)image.sprite != (Object)(object)Skin.Panel && !KitArt.IsPanel(image.sprite)) { Prune(); panelSprites[image] = image.sprite; panelMaterials[image] = ((Graphic)image).material; } Sprite panel = KitArt.Panel; bool timber = KitArt.Timber; image.sprite = (Sprite)((!on || timber) ? GameSprite(image) : (((Object)(object)panel != (Object)null) ? ((object)panel) : ((object)Skin.Panel))); ((Graphic)image).material = ((!on || timber) ? GameMaterial(image) : null); image.type = (Type)1; ((Graphic)image).color = Color.white; TimberBackground.Apply(image, on && timber && (Object)(object)panel != (Object)null); NightShade.Track((Graphic)(object)image); } } public static void Cell(Image image) { Cell(image, On); } public static void Cell(Image image, bool on) { if (!((Object)(object)image == (Object)null)) { if ((Object)(object)gameCell == (Object)null && (Object)(object)image.sprite != (Object)null && (Object)(object)image.sprite != (Object)(object)Skin.Cell && (Object)(object)image.sprite != (Object)(object)KitArt.Cell) { gameCell = image.sprite; } Sprite cell = KitArt.Cell; image.sprite = (Sprite)((!on) ? gameCell : (((Object)(object)cell != (Object)null) ? ((object)cell) : ((object)Skin.Cell))); image.type = (Type)((!on || !((Object)(object)cell != (Object)null)) ? 1 : 0); } } public static Sprite GameSprite(Image image) { if (!((Object)(object)image != (Object)null) || !panelSprites.TryGetValue(image, out Sprite value)) { if (image == null) { return null; } return image.sprite; } return value; } public static Material GameMaterial(Image image) { if (!((Object)(object)image != (Object)null) || !panelMaterials.TryGetValue(image, out Material value)) { if (image == null) { return null; } return ((Graphic)image).material; } return value; } public static Image Background(RectTransform panel) { Transform val = (((Object)(object)panel != (Object)null) ? ((Transform)panel).Find("Bkg") : null); if (!((Object)(object)val != (Object)null)) { return null; } return ((Component)val).GetComponent<Image>(); } private static void Prune() { gone.Clear(); foreach (Image key in panelSprites.Keys) { if ((Object)(object)key == (Object)null) { gone.Add(key); } } foreach (Image item in owned) { if ((Object)(object)item == (Object)null) { gone.Add(item); } } foreach (Image item2 in gone) { panelSprites.Remove(item2); panelMaterials.Remove(item2); owned.Remove(item2); } gone.Clear(); } private static void Hook() { if (!hooked) { hooked = true; SceneManager.sceneLoaded += delegate { Prune(); }; } } } public static class NightShade { private static readonly int SunId = Shader.PropertyToID("_SunColor"); private static readonly int AmbientId = Shader.PropertyToID("_AmbientColor"); private static readonly List<Graphic> shaded = new List<Graphic>(); private static readonly HashSet<Graphic> known = new HashSet<Graphic>(); private static float current = 1f; public static void Track(Graphic graphic) { if (!((Object)(object)graphic == (Object)null)) { if (known.Add(graphic)) { shaded.Add(graphic); } Paint(graphic, current); } } public static void Update() { float num = Target(); if (!NightShadeCurve.Moves(current, num)) { return; } current = NightShadeCurve.Step(current, num, Time.unscaledDeltaTime); for (int num2 = shaded.Count - 1; num2 >= 0; num2--) { Graphic val = shaded[num2]; if ((Object)(object)val == (Object)null) { known.Remove(val); shaded.RemoveAt(num2); } else { Paint(val, current); } } } private static float Target() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) float night = KitLook.Night; if (night >= 1f || !KitLook.On || (Object)(object)EnvMan.instance == (Object)null) { return 1f; } Color globalColor = Shader.GetGlobalColor(AmbientId); Color globalColor2 = Shader.GetGlobalColor(SunId); return NightShadeCurve.Target(night, true, true, globalColor.r, globalColor.g, globalColor.b, globalColor2.r, globalColor2.g, globalColor2.b); } private static void Paint(Graphic graphic, float shade) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) float num = (((Object)(object)graphic.material == (Object)(object)Graphic.defaultGraphicMaterial) ? shade : 1f); Color val = default(Color); ((Color)(ref val))..ctor(num, num, num, 1f); if (graphic.canvasRenderer.GetColor() != val) { graphic.canvasRenderer.SetColor(val); } } } public static class RoundedFill { public const float Radius = 6f; private const int Corner = 16; private const int Size = 34; private static Sprite sprite; public static Sprite Sprite { get { if (!((Object)(object)sprite != (Object)null)) { return sprite = Paint(); } return sprite; } } private static Sprite Paint() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_002c: 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_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_0091: Expected O, but got Unknown Texture2D val = new Texture2D(34, 34, (TextureFormat)4, false) { name = "Crucible_rounded", filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, hideFlags = (HideFlags)32 }; val.SetPixels32(KitPaintUnity.White(KitPaint.RoundedAlpha())); val.Apply(false, true); Sprite obj = Sprite.Create(val, new Rect(0f, 0f, 34f, 34f), new Vector2(0.5f, 0.5f), 266.66666f, 0u, (SpriteMeshType)0, new Vector4(16f, 16f, 16f, 16f)); ((Object)obj).name = "Crucible_rounded"; ((Object)obj).hideFlags = (HideFlags)32; return obj; } } internal static class KitPaintUnity { public static Color32[] White(byte[] alpha) { //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) Color32[] array = (Color32[])(object)new Color32[alpha.Length]; for (int i = 0; i < alpha.Length; i++) { array[i] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, alpha[i]); } return array; } } public sealed class TimberBackground : MaskableGraphic { private const string Name = "Crucible_timberwood"; private static readonly Color PanelTint = KitUnity.Colour(KitPalette.PanelTint); private static readonly HashSet<string> roundNames = new HashSet<string>(); private static readonly HashSet<Image> roundImages = new HashSet<Image>(); private static readonly HashSet<Image> forgedImages = new HashSet<Image>(); private Image frame; private Sprite source; private bool originalFill; private Sprite originalSprite; private Material originalMaterial; private Type originalType; private Color originalColor; private bool round; private bool straight; private readonly List<Vector2> contour = new List<Vector2>(); private readonly List<Vector2> normals = new List<Vector2>(); private readonly List<Vector2> interior = new List<Vector2>(); private readonly List<int> triangles = new List<int>(); private Matrix4x4 lastMatrix; private Rect lastCanvas; private int frameVersion = -1; private Rect geometryRect; private bool geometryRound; private bool geometryStraight; private int geometryVersion = -1; private float geometryWidth; public override Texture mainTexture { get { if (!((Object)(object)source != (Object)null)) { return ((Graphic)this).mainTexture; } return (Texture)(object)source.texture; } } private RectTransform Root { get { if (!((Object)(object)((Graphic)this).canvas != (Object)null)) { return null; } Transform transform = ((Component)((Graphic)this).canvas.rootCanvas).transform; return (RectTransform)(object)((transform is RectTransform) ? transform : null); } } public static void RegisterRound(string imageName) { if (!string.IsNullOrEmpty(imageName)) { roundNames.Add(imageName); } } public static void RegisterRound(Image image) { if ((Object)(object)image != (Object)null) { roundImages.RemoveWhere((Image known) => (Object)(object)known == (Object)null); roundImages.Add(image); } } public static void RegisterForged(Image image) { if ((Object)(object)image != (Object)null) { forgedImages.RemoveWhere((Image known) => (Object)(object)known == (Object)null); forgedImages.Add(image); } } private static Sprite WallpaperFor(Image image) { if (!forgedImages.Contains(image) || !((Object)(object)KitForgedArt.WindowWood != (Object)null)) { return KitArt.Wallpaper; } return KitForgedArt.WindowWood; } public static void Apply(Image image, bool on) { //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0131: 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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018b: 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_00c2: 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_0210: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) Transform val = ((Component)image).transform.Find("Crucible_timberwood"); TimberBackground timberBackground = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TimberBackground>() : null); if (!on) { if ((Object)(object)timberBackground != (Object)null && ((Component)timberBackground).gameObject.activeSelf) { image.fillCenter = timberBackground.originalFill; image.sprite = timberBackground.originalSprite; ((Graphic)image).material = timberBackground.originalMaterial; image.type = timberBackground.originalType; ((Graphic)image).color = timberBackground.originalColor; ((Component)timberBackground).gameObject.SetActive(false); } } else if (!((Object)(object)timberBackground != (Object)null) || !((Component)timberBackground).gameObject.activeSelf || !(((Graphic)image).color == Color.clear) || !((Object)(object)timberBackground.source == (Object)(object)WallpaperFor(image)) || !(((Graphic)timberBackground).color == PanelTint)) { if ((Object)(object)timberBackground == (Object)null) { GameObject val2 = new GameObject("Crucible_timberwood", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TimberBackground) }); val2.transform.SetParent(((Component)image).transform, false); val2.transform.SetAsFirstSibling(); timberBackground = val2.GetComponent<TimberBackground>(); timberBackground.frame = image; ((Graphic)timberBackground).raycastTarget = false; val2.SetActive(false); } if (!((Component)timberBackground).gameObject.activeSelf) { timberBackground.originalFill = image.fillCenter; timberBackground.originalSprite = image.sprite; timberBackground.originalMaterial = ((Graphic)image).material; timberBackground.originalType = image.type; timberBackground.originalColor = ((Graphic)image).color; } timberBackground.round = roundImages.Contains(image) || roundNames.Contains(((Object)image).name); timberBackground.straight = !timberBackground.round && forgedImages.Contains(image); Transform val3 = ((Component)image).transform.Find("Crucible_frame"); if ((Object)(object)val3 != (Object)null) { ((Component)val3).gameObject.SetActive(false); } timberBackground.frameVersion = TimberFrame.Version; ((Graphic)image).material = null; ((Graphic)image).color = Color.clear; image.fillCenter = false; timberBackground.source = WallpaperFor(image); ((Graphic)timberBackground).color = PanelTint; ((Component)timberBackground).gameObject.SetActive(true); timberBackground.Inset(); ((Graphic)timberBackground).SetAllDirty(); NightShade.Track((Graphic)(object)timberBackground); } } private void Inset() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_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) if (!((Object)(object)frame == (Object)null)) { RectTransform rectTransform = ((Graphic)this).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; Vector2 offsetMin = (rectTransform.offsetMax = Vector2.zero); rectTransform.offsetMin = offsetMin; } } private void LateUpdate() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) TimberFrame.Poll(); if (frameVersion != TimberFrame.Version) { frameVersion = TimberFrame.Version; Inset(); ((Graphic)this).SetAllDirty(); } RectTransform root = Root; if (!((Object)(object)root == (Object)null) && !((Object)(object)frame == (Object)null) && (((Transform)root).worldToLocalMatrix * ((Transform)((Graphic)this).rectTransform).localToWorldMatrix != lastMatrix || root.rect != lastCanvas)) { ((Graphic)this).SetVerticesDirty(); } } protected override void OnPopulateMesh(VertexHelper mesh) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_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_0081: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: 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_00cb: 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_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) mesh.Clear(); RectTransform root = Root; if (!((Object)(object)source == (Object)null) && !((Object)(object)root == (Object)null)) { Rect rect = ((Graphic)this).rectTransform.rect; if (!(((Rect)(ref rect)).width <= 0f) && !(((Rect)(ref rect)).height <= 0f)) { lastMatrix = ((Transform)root).worldToLocalMatrix * ((Transform)((Graphic)this).rectTransform).localToWorldMatrix; lastCanvas = root.rect; Vector4 border = source.border; Rect rect2 = source.rect; Rect wood = Rect.MinMaxRect(((Rect)(ref rect2)).xMin + border.x, ((Rect)(ref rect2)).yMin + border.y, ((Rect)(ref rect2)).xMax - border.z, ((Rect)(ref rect2)).yMax - border.w); Texture val = ((Graphic)this).mainTexture; Vector2 textureSize = default(Vector2); ((Vector2)(ref textureSize))..ctor((float)val.width, (float)val.height); WoodPanel(mesh, rect, wood, textureSize); } } } private void WoodPanel(VertexHelper mesh, Rect rect, Rect wood, Vector2 textureSize) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: 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_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: 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_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0195: 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_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) if (geometryVersion != TimberFrame.Version || geometryRect != rect || geometryRound != round || geometryStraight != straight) { float roughness = (straight ? 0f : TimberFrame.Roughness); TimberFrame.Contour(rect, round, roughness, contour, normals); geometryWidth = TimberFrame.BevelWidth(rect, round, roughness); interior.Clear(); for (int i = 0; i < contour.Count; i++) { interior.Add(contour[i] - normals[i] * geometryWidth); } TimberFrame.Triangulate(interior, triangles); geometryRect = rect; geometryRound = round; geometryStraight = straight; geometryVersion = TimberFrame.Version; } int count = contour.Count; float num = geometryWidth; for (int j = 0; j < 4; j++) { float num2 = TimberGeometry.RingDepth(j, num); float num3 = TimberGeometry.RingAlpha(j); for (int k = 0; k < count; k++) { Vector2 val = normals[k]; float num4 = TimberGeometry.RingShade(j, new KitVector(val.x, val.y)); Color tint = ((Graphic)this).color * new Color(num4, num4, num4, num3); Vertex(mesh, contour[k] - val * num2, wood, textureSize, tint); } } for (int l = 0; l < 3; l++) { for (int m = 0; m < count; m++) { int num5 = (m + 1) % count; int num6 = l * count; int num7 = num6 + count; mesh.AddTriangle(num6 + m, num6 + num5, num7 + num5); mesh.AddTriangle(num6 + m, num7 + num5, num7 + m); } } for (int n = 0; n < triangles.Count; n += 3) { mesh.AddTriangle(3 * count + triangles[n], 3 * count + triangles[n + 1], 3 * count + triangles[n + 2]); } } private void Vertex(VertexHelper mesh, Vector2 point, Rect wood, Vector2 textureSize, Color tint) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_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) Vector2 point2 = Vector2.op_Implicit(((Matrix4x4)(ref lastMatrix)).MultiplyPoint3x4(Vector2.op_Implicit(point))); mesh.AddVert(Vector2.op_Implicit(point), Color32.op_Implicit(tint), Vector4.op_Implicit(TimberFrame.Uv(point2, lastCanvas, wood, textureSize))); } } public static class TimberFrame { private static readonly List<KitVector> points = new List<KitVector>(); private static readonly List<KitVector> directions = new List<KitVector>(); private static readonly List<KitVector> polygon = new List<KitVector>(); private static bool pending; public static float Width => KitLook.Width; internal static float Roughness => KitLook.Jaggedness; public static int Version { get; private set; } public static float BevelWidth(Rect rect, bool round) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return BevelWidth(rect, round, Roughness); } public static float BevelWidth(Rect rect, bool round, float roughness) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) return TimberGeometry.BevelWidth(KitUnity.Rect(rect), round, Width, roughness); } public static void Invalidate() { pending = true; } public static void Poll() { if (pending) { pending = false; Version++; } } public static bool Owns(Sprite sprite) { if ((Object)(object)sprite != (Object)null) { if (!(((Object)sprite).name == "Crucible_frame")) { return ((Object)sprite).name == KitArtCatalog.SpriteName("ringframe"); } return true; } return false; } public static void Contour(Rect rect, bool round, List<Vector2> contour, List<Vector2> normals) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Contour(rect, round, Roughness, contour, normals); } public static void Contour(Rect rect, bool round, float roughness, List<Vector2> contour, List<Vector2> normals) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) TimberGeometry.Contour(KitUnity.Rect(rect), round, roughness, points, directions); contour.Clear(); normals.Clear(); for (int i = 0; i < points.Count; i++) { contour.Add(KitUnity.Vector(points[i])); normals.Add(KitUnity.Vector(directions[i])); } } public static void Triangulate(List<Vector2> interior, List<int> triangles) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_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) polygon.Clear(); foreach (Vector2 item in interior) { polygon.Add(new KitVector(item.x, item.y)); } TimberGeometry.Triangulate((IReadOnlyList<KitVector>)polygon, triangles); } public static Vector2 Uv(Vector2 point, Rect canvas, Rect wood, Vector2 textureSize) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //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) return KitUnity.Vector(TimberGeometry.WallpaperUv(new KitVector(point.x, point.y), KitUnity.Rect(canvas), KitUnity.Rect(wood), new KitVector(textureSize.x, textureSize.y))); } } public static class BlockedCell { private const string Name = "Crucible_blocked"; private const float Dimmed = 0.55f; private const float Inset = 8f; private static readonly Color Grey = KitUnity.Colour(KitPalette.BlockedCross); public static void Mark(GameObject cell, bool blocked) { if (!((Object)(object)cell == (Object)null)) { CanvasGroup val = cell.GetComponent<CanvasGroup>(); if ((Object)(object)val == (Object)null && blocked) { val = cell.AddComponent<CanvasGroup>(); } if ((Object)(object)val != (Object)null) { val.alpha = (blocked ? 0.55f : 1f); val.blocksRaycasts = !blocked; val.interactable = !blocked; } Transform val2 = cell.transform.Find("Crucible_blocked"); if ((Object)(object)val2 == (Object)null && blocked) { val2 = Cross(cell.transform); } if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(blocked); } } } public static bool IsBlocked(GameObject cell) { Transform val = (((Object)(object)cell != (Object)null) ? cell.transform.Find("Crucible_blocked") : null); if ((Object)(object)val != (Object)null) { return ((Component)val).gameObject.activeSelf; } return false; } private static Transform Cross(Transform cell) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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_007e: 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_00ae: Expected O, but got Unknown GameObject val = new GameObject("Crucible_blocked", new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(cell, false); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = new Vector2(8f, 8f); val2.offsetMax = new Vector2(-8f, -8f); ((Transform)val2).SetAsLastSibling(); Image component = val.GetComponent<Image>(); component.sprite = CrossMark.Sprite; ((Graphic)component).color = Grey; component.preserveAspect = true; ((Graphic)component).raycastTarget = false; return (Transform)val2; } } public static class ButtonStrip { public const string OpenKeepName = "PackPanel_buttonstrip"; public const float Height = 30f; public static RectTransform Mark(RectTransform panel, bool on, string name = "PackPanel_buttonstrip") { Transform obj = ((Transform)panel).Find(name); RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null); if ((Object)(object)val == (Object)null) { if (!on) { return null; } val = Make(panel, name); } if (((Component)val).gameObject.activeSelf != on) { ((Component)val).gameObject.SetActive(on); } return val; } private static RectTransform Make(RectTransform panel, string name) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_0034: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }) { layer = ((Component)panel).gameObject.layer }.transform; ((Transform)val).SetParent((Transform)(object)panel, false); val.anchorMin = Vector2.zero; val.anchorMax = new Vector2(1f, 0f); val.pivot = new Vector2(0.5f, 0f); val.anchoredPosition = Vector2.zero; val.sizeDelta = new Vector2(0f, 30f); return val; } } public static class Divider { public const string DefaultName = "Crucible_divider"; private const float Thickness = 2f; private static readonly Color Bronze = KitUnity.Colour(KitPalette.Divider); public static RectTransform Place(RectTransform panel, Vector2 left, float width, bool show, string name = "Crucible_divider") { //IL_001d: 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_0047: 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) //IL_0069: 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) Transform val = ((Transform)panel).Find(name); object obj = ((!((Object)(object)val != (Object)null)) ? ((object)Make(panel, name)) : ((object)(RectTransform)val)); ((Component)obj).gameObject.SetActive(show); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0f, 1f); ((RectTransform)obj).anchorMax = val2; ((RectTransform)obj).anchorMin = val2; ((RectTransform)obj).pivot = new Vector2(0f, 0.5f); ((RectTransform)obj).sizeDelta = new Vector2(width, 2f); ((RectTransform)obj).anchoredPosition = left; return (RectTransform)obj; } private static RectTransform Make(RectTransform panel, string name) { //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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[2] { typeof(RectTransform), typeof(Image) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent((Transform)(object)panel, false); Image component = val.GetComponent<Image>(); ((Graphic)component).color = Bronze; ((Graphic)component).raycastTarget = false; return val2; } } public static class GameButtons { private static readonly Color LitText = KitUnity.Colour(KitPalette.TabLitText); private static readonly Color Lit = KitUnity.Colour(KitPalette.TabLit); private static readonly Color Dim = KitUnity.Colour(KitPalette.TabDim); private static readonly Color DimText = KitUnity.Colour(KitPalette.TabDimText); public static Button Source { get { InventoryGui instance = InventoryGui.instance; if (!((Object)(object)instance != (Object)null) || !((Object)(object)instance.m_takeAllButton != (Object)null)) { return null; } return instance.m_takeAllButton; } } public static Button Find(RectTransform panel, string name, string word, Action click) { Transform val = ((Transform)panel).Find(name); if ((Object)(object)val != (Object)null) { Button component = ((Component)val).GetComponent<Button>(); KitForged.Button(component); return component; } Button source = Source; if (!((Object)(object)source != (Object)null)) { return null; } return Make(source, panel, name, word, click); } public static void Set(Button button, Vector2 at, float width, float height, bool lit, bool usable) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_003d: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)button == (Object)null)) { RectTransform val = (RectTransform)((Component)button).transform; Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0f, 1f); val.pivot = val2; Vector2 anchorMin = (val.anchorMax = val2); val.anchorMin = anchorMin; val.anchoredPosition = at; val.sizeDelta = new Vector2(width, height); ((Transform)val).localScale = Vector3.one; ((Selectable)button).interactable = usable; Tint(button, lit); KitForged.Button(button); } } public static void Tint(Button button, bool lit) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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) Image component = ((Component)button).GetComponent<Image>(); if ((Object)(object)component != (Object)null) { ((Graphic)component).color = (lit ? Lit : Dim); } TMP_Text componentInChildren = ((Component)button).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren != (Object)null) { ((Graphic)componentInChildren).color = (lit ? LitText : DimText); } } public static Button Make(Button source, RectTransform panel, string name, string word, Action click) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0066: 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) GameObject val = Object.Instantiate<GameObject>(((Component)source).gameObject, (Transform)(object)panel); ((Object)val).name = name; val.SetActive(true); Strip(val); Button component = val.GetComponent<Button>(); component.onClick = new ButtonClickedEvent(); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { Run(name, click); }); Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)component).navigation = navigation; Caption(val, word); KitForged.Button(component); return component; } public static void Strip(GameObject go) { UIGamePad[] componentsInChildren = go.GetComponentsInChildren<UIGamePad>(true); foreach (UIGamePad val in componentsInChildren) { if ((Object)(object)val.m_hint != (Object)null && val.m_hint.transform.IsChildOf(go.transform)) { Object.DestroyImmediate((Object)(object)val.m_hint); } Object.DestroyImmediate((Object)(object)val); } ButtonTextColor[] componentsInChildren2 = go.GetComponentsInChildren<ButtonTextColor>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } ButtonImageColor[] componentsInChildren3 = go.GetComponentsInChildren<ButtonImageColor>(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren3[i]); } Localize[] componentsInChildren4 = go.GetComponentsInChildren<Localize>(true); for (int i = 0; i < componentsInChildren4.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren4[i]); } } public static void Caption(GameObject go, string word) { //IL_004a: 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) TMP_Text componentInChildren = go.GetComponentInChildren<TMP_Text>(true); if (!((Object)(object)componentInChildren == (Object)null)) { componentInChildren.text = KitLocalize.Text(word); componentInChildren.enableAutoSizing = true; componentInChildren.fontSizeMin = 9f; componentInChildren.fontSizeMax = 16f; componentInChildren.textWrappingMode = (TextWrappingModes)0; componentInChildren.overflowMode = (TextOverflowModes)3; componentInChildren.margin = KitUnity.Vector4(KitButtonText.Margin); } } private static void Run(string name, Action click) { try { click?.Invoke(); } catch (Exception arg) { MeadworksLog log = CrucibleUiPlugin.Log; if (log != null) { log.Error((object)$"Crucible.UI: button {name} failed: {arg}"); } } } } public sealed class PanelFind { private readonly string name; private Transform foundIn; private RectTransform found; public PanelFind(string name) { this.name = name; } public RectTransform In(InventoryGui gui) { if (!((Object)(object)gui != (Object)null)) { return null; } return In((Transform)(object)gui.m_player); } public RectTransform In(Transform parent) { if ((Object)(object)parent == (Object)null) { return null; } if (parent == foundIn && (found == null || (Object)(object)found != (Object)null)) { return found; } foundIn = parent; ref RectTransform reference = ref found; Transform obj = parent.Find(name); reference = (RectTransform)(object)((obj is RectTransform) ? obj : null); return found; } public void Made(InventoryGui gui, RectTransform panel) { Made((Transform)(object)(((Object)(object)gui != (Object)null) ? gui.m_player : null), panel); } public void Made(Transform parent, RectTransform panel) { foundIn = parent; found = panel; } } public static class PopupPlace { private static readonly Vector3[] corners = (Vector3[])(object)new Vector3[4]; private static RectTransform backgroundOf; private static Image background; public static void Under(InventoryGui gui, RectTransform popup, float slotLeft, float centre, float top, float gap) { //IL_001f: 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) //IL_0059: 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_006e: Unknown result type (might be due to invalid IL or missing references) bool flag = gui.IsContainerOpen() && ((Component)gui.m_container).gameObject.activeInHierarchy; float num = KitPlacement.PopupX(slotLeft, centre, popup.sizeDelta.x, flag, flag ? RightEdge(gui, gui.m_container) : 0f, gap); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(num, top); Vector2 val2 = popup.anchoredPosition - val; if (((Vector2)(ref val2)).sqrMagnitude > 0.01f) { popup.anchoredPosition = val; } } private static float RightEdge(InventoryGui gui, RectTransform panel) { //IL_004e: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)panel != (Object)(object)backgroundOf) { backgroundOf = panel; background = KitSkin.Background(panel); } ((RectTransform)(((Object)(object)background != (Object)null) ? ((object)((Graphic)background).rectTransform) : ((object)panel))).GetWorldCorners(corners); float x = ((Transform)gui.m_player).InverseTransformPoint(corners[2]).x; Rect rect = gui.m_player.rect; return x - ((Rect)(ref rect)).xMax; } } public sealed class RectMemo { private readonly RectTransform rect; private readonly Vector2 position; private readonly Vector2 size; public Vector2 Size => size; public bool Alive => (Object)(object)rect != (Object)null; private RectMemo(RectTransform rect) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) this.rect = rect; position = rect.anchoredPosition; size = rect.sizeDelta; } public static RectMemo Of(Transform transform) { RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (val == null) { return null; } return new RectMemo(val); } public void Set(Vector2 move, Vector2 grow) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_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_004e: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rect == (Object)null)) { Vector2 val = position + move; Vector2 val2 = size + grow; if (rect.anchoredPosition != val) { rect.anchoredPosition = val; } if (rect.sizeDelta != val2) { rect.sizeDelta = val2; } } } } public static class SheetScroll { private const float BarWidth = 4f; private const float FallbackSensitivity = 30f; private static readonly Color Track = KitUnity.Colour(KitPalette.ScrollTrack); private static readonly Color Handle = KitUnity.Colour(KitPalette.ScrollHandle); public static RectTransform Make(RectTransform panel, float margin, float top) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) RectTransform val = Child((Transform)(object)panel, "viewport"); Stretch(val, new Vector2(margin, margin), new Vector2(0f - margin, 0f - top)); ((Graphic)((Component)val).gameObject.AddComponent<Image>()).color = Color.clear; ((Component)val).gameObject.AddComponent<RectMask2D>(); RectTransform val2 = Child((Transform)(object)val, "content"); val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = Vector2.one; val2.pivot = new Vector2(0.5f, 1f); val2.sizeDelta = Vector2.zero; ScrollRect obj = ((Component)val).gameObject.AddComponent<ScrollRect>(); obj.viewport = val; obj.content = val2; obj.horizontal = false; obj.movementType = (MovementType)2; obj.scrollSensitivity = GameSensitivity(InventoryGui.instance); obj.verticalScrollbar = Bar(panel, margin, top); obj.verticalScrollbarVisibility = (ScrollbarVisibility)1; return val2; } private static float GameSensitivity(InventoryGui gui) { ScrollRect val = (((Object)(object)gui != (Object)null && (Object)(object)gui.m_crafting != (Object)null) ? ((Component)gui.m_crafting).GetComponentInChildren<ScrollRect>(true) : null); if (!((Object)(object)val != (Object)null) || !(val.scrollSensitivity > 0f)) { return 30f; } return val.scrollSensitivity; } private static Scrollbar Bar(RectTransform panel, float margin, float top) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00b3: 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_00ed: Unknown result type (might be due to invalid IL or missing references) RectTransform obj = Child((Transform)(object)panel, "scrollbar"); obj.anchorMin = new Vector2(1f, 0f); obj.anchorMax = Vector2.one; obj.pivot = new Vector2(0.5f, 0.5f); obj.offsetMin = new Vector2((0f - (margin + 4f)) / 2f, margin); obj.offsetMax = new Vector2((0f - (margin - 4f)) / 2f, 0f - top); ((Graphic)((Component)obj).gameObject.AddComponent<Image>()).color = Track; RectTransform val = Child((Transform)(object)obj, "handle"); Stretch(val, Vector2.zero, Vector2.zero); Image val2 = ((Component)val).gameObject.AddComponent<Image>(); ((Graphic)val2).color = Handle; Scrollbar obj2 = ((Component)obj).gameObject.AddComponent<Scrollbar>(); obj2.direction = (Direction)2; obj2.handleRect = val; ((Selectable)obj2).targetGraphic = (Graphic)(object)val2; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)obj2).navigation = navigation; return obj2; } private static RectTransform Child(Transform parent, string name) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }).transform; ((Transform)val).SetParent(parent, false); return val; } private static void Stretch(RectTransform rect, Vector2 min, Vector2 max) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = min; rect.offsetMax = max; } } public static class StatRows { public const float Size = 12f; private const float HeadingSize = 10.5f; private const float SectionSpace = 5f; private static readonly Color LabelColour = KitUnity.Colour(KitPalette.SheetLabel); private static readonly Color ValueColour = KitUnity.Colour(KitPalette.SheetValue); private static readonly Color HeadingColour = KitUnity.Colour(KitPalette.SheetHeading); public static void Write(RectTransform content, KitSheet sheet, float width, TMP_FontAsset font) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) float num = 0f; int i; for (i = 0; i < sheet.Lines.Count; i++) { Line val = sheet.Lines[i]; if (val.SpaceBefore) { num += 5f; } num += Lay(Row(content, i, font), val, num, width); } for (; i < ((Transform)content).childCount; i++) { ((Component)((Transform)content).GetChild(i)).gameObject.SetActive(false); } content.sizeDelta = new Vector2(content.sizeDelta.x, num); } private static float Lay(RectTransform row, Line line, float y, float width) { //IL_0026: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_006e: 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_00a7: 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) ((Component)row).gameObject.SetActive(true); TMP_Text component = ((Component)((Transform)row).GetChild(0)).GetComponent<TMP_Text>(); TMP_Text component2 = ((Component)((Transform)row).GetChild(1)).GetComponent<TMP_Text>(); Style(component, line.Heading ? 10.5f : 12f, line.Heading ? HeadingColour : LabelColour, wraps: true); Style(component2, 12f, ValueColour, wraps: false); component.text = KitLocalize.Text(line.Heading ? ("<uppercase>" + line.Label + "</uppercase>") : line.Label); component2.text = KitLocalize.Text(line.Value); Tip(row, line); float num = ((line.Value.Length > 0) ? component2.GetPreferredValues().x : 0f); float num2 = KitRowLayout.LabelWidth(width, num); float y2 = component.GetPreferredValues(num2, 0f).y; Put(row, 0f, y, width, y2); Put(component.rectTransform, 0f, 0f, num2, y2); Put(component2.rectTransform, width - num, 0f, num, y2); return y2; } private static void Style(TMP_Text text, float size, Color colour, bool wraps) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) text.enableAutoSizing = false; text.fontSize = size; ((Graphic)text).color = colour; text.textWrappingMode = (TextWrappingModes)(wraps ? 1 : 0); text.overflowMode = (TextOverflowModes)0; text.alignment = (TextAlignmentOptions)(wraps ? 257 : 260); text.margin = Vector4.zero; text.richText = true; ((Graphic)text).raycastTarget = false; } private static RectTransform Row(RectTransform content, int index, TMP_FontAsset font) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown if (index >= ((Transform)content).childCount) { RectTransform obj = Child((Transform)(object)content, "row"); Hover(obj); Text(obj, "label", font); Text(obj, "value", font); return obj; } return (RectTransform)((Transform)content).GetChild(index); } private static void Hover(RectTransform row) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ((Graphic)((Component)row).gameObject.AddComponent<Image>()).color = Color.clear; KitTooltip.Attach(((Component)row).gameObject); } private static void Tip(RectTransform row, Line line) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_0043: Unknown result type (might be due to invalid IL or missing references) UITooltip component = ((Component)row).GetComponent<UITooltip>(); if ((Object)(object)component != (Object)null) { component.Set((line.Tip != null) ? KitLocalize.Text(line.Label) : "", line.Tip ?? "", (RectTransform)null, default(Vector2)); } } private static void Text(RectTransform row, string name, TMP_FontAsset font) { GameObject gameObject = ((Component)Child((Transform)(object)row, name)).gameObject; gameObject.SetActive(false); TextMeshProUGUI val = gameObject.AddComponent<TextMeshProUGUI>(); if ((Object)(object)font != (Object)null) { ((TMP_Text)val).font = font; } gameObject.SetActive(true); ((TMP_Text)val).fontSize = 12f; } private static RectTransform Child(Transform parent, string name) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }).transform; ((Transform)val).SetParent(parent, false); return val; } private static void Put(RectTransform rect, float x, float y, float width, float height) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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_003a: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0f, 1f); rect.pivot = val; Vector2 anchorMin = (rect.anchorMax = val); rect.anchorMin = anchorMin; rect.anchoredPosition = new Vector2(x, 0f - y); rect.sizeDelta = new Vector2(width, height); } } [BepInPlugin("com.meadworks.crucible.ui", "ExtraPotions Meadworks: Crucible UI", "0.3.23")] [BepInDependency("com.meadworks.crucible", "0.3.34")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class CrucibleUiPlugin : BaseUnityPlugin { public const string Guid = "com.meadworks.crucible.ui"; public const string Name = "ExtraPotions Meadworks: Crucible UI"; public const string Version = "0.3.23"; public const string Product = "crucible_ui"; private Harmony harmony; private MeadworksTranslations texts; internal static MeadworksLog Log { get; private set; } public static bool Active { get; private set; } public static string Language => MeadworksTranslations.GameLanguage(); private void Awake() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown Log = MeadworksLog.For(((BaseUnityPlugin)this).Logger, "crucible_ui"); if (!MeadworksHeadless.AllowsUi) { Log.Info("Crucible.UI idle: headless process, no UI is created."); return; } KitCanvas.HangulWordBreaks(); KitLook.Bind(((BaseUnityPlugin)this).Config); KitNoticePanel.Bind(((BaseUnityPlugin)this).Config); MeadworksPauseMenu.Bind(((BaseUnityPlugin)this).Config); harmony = new Harmony("com.meadworks.crucible.ui"); harmony.PatchAll(typeof(CrucibleUiPlugin).Assembly); GameWindow.Install(harmony); GamePanelTheme.Initialize(); KitNativeMenus.Initialize(); KitNativeDialogs.Initialize(); KitNativeNotices.Initialize(); try { texts = MeadworksTranslations.Register(((BaseUnityPlugin)this).Info.Metadata, KitText.Default, (string)null, ((BaseUnityPlugin)this).Logger); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Kit texts stay built-in; translation overrides unavailable: " + ex.GetType().Name)); } SceneManager.sceneUnloaded += SceneUnloaded; Active = true; KitDialogs.Initialize(); KitHudLayout.Initialize(((BaseUnityPlugin)this).Config); Log.Info("Crucible.UI 0.3.23 ready (PackPanel 0.12.0 UI kit)"); } private void Update() { if (Active) { KitCanvas.HangulWordBreaks(); KitDialogs.Tick(); KitNativeMenus.Tick(); GamePanelTheme.Update(); KitNativeDialogs.Tick(); NightShade.Update(); KitMapFrames.Update(); KitInventoryScreen.Update(); KitUnderInventoryDock.Update(); KitWindow.Tick(); KitTextField.Tick(); KitStepper.Tick(); KitBuildTab.TickAll(); MeadworksPauseMenu.Tick(); MeadworksTranslations obj = texts; if (obj != null) { obj.Update(); } } } private void LateUpdate() { if (Active) { KitTextField.LateTick(); KitHudLayout.Tick(); KitNativeNotices.Tick(); KitNoticePanel.Tick(); KitNativeMenus.LateTickNotices(); } } private void SceneUnloaded(Scene scene) { KitTooltip.Prune(); KitForged.Prune(); KitNoticePanel.Reset(); } private void OnDestroy() { SceneManager.sceneUnloaded -= SceneUnloaded; Active = false; KitNoticePanel.Reset(); KitHudLayout.Shutdown(); KitDialogs.Shutdown(); KitNativeMenus.Shutdown(); KitNativeDialogs.Shutdown(); KitNativeNotices.Shutdown(); KitTooltip.Shutdown(); KitBuildTab.Shutdown(); MeadworksPauseMenu.Shutdown(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } MeadworksTranslations obj2 = texts; if (obj2 != null) { obj2.Dispose(); } } } public sealed class KitAlmanacColor : MonoBehaviour, IPointerDownHandler, IEventSystemHandler, IDragHandler { private KitAlmanacDialog dialog; private RawImage square; private Image preview; private RectTransform marker; private Texture2D texture; private Texture2D hueTexture; private Color value; private float hue; private float saturation; private float brightness; private KitAlmanacNumber hueField; private KitTextField hex; private readonly KitAlmanacNumber[] components = new KitAlmanacNumber[4]; private bool changing; private bool cancelled; private Action<Color> commit; public static void Open(KitWindow parent, string title, Color initial, Action<Color> commit) { //IL_0013: 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_0056: Expected O, but got Unknown //IL_0073: 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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00db: 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_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_015d: 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_0192: Expected O, but got Unknown //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Expected O, but got Unknown //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_03f4: Unknown result type (might be due to invalid IL or missing references) //IL_0403: Unknown result type (might be due to invalid IL or missing references) //IL_04bc: Unknown result type (might be due to invalid IL or missing references) //IL_04cb: Unknown result type (might be due to invalid IL or missing references) //IL_0579: Unknown result type (might be due to invalid IL or missing references) //IL_0588: Unknown result type (might be due to invalid IL or missing references) //IL_05bf: Unknown result type (might be due to invalid IL or missing references) //IL_05ce: Unknown result type (might be due to invalid IL or missing references) KitAlmanacDialog dialog = KitAlmanacDialog.Create(parent, title, new Vector2(620f, 580f)); if (dialog == null) { return; } GameObject val = new GameObject("SaturationValue", new Type[2] { typeof(RectTransform), typeof(RawImage) }); val.transform.SetParent((Transform)(object)dialog.Panel, false); KitAlmanacDialog.Place((RectTransform)val.transform, new Vector2(20f, -60f), new Vector2(235f, 235f)); KitAlmanacColor picker = val.AddComponent<KitAlmanacColor>(); picker.dialog = dialog; picker.commit = commit; picker.square = val.GetComponent<RawImage>(); picker.value = initial; GameObject val2 = new GameObject("Preview", new Type[2] { typeof(RectTransform), typeof(Image) }); val2.transform.SetParent((Transform)(object)dialog.Panel, false); picker.preview = val2.GetComponent<Image>(); KitAlmanacDialog.Place(((Graphic)picker.preview).rectTransform, new Vector2(20f, -310f), new Vector2(235f, 32f)); GameObject val3 = new GameObject("Selection", new Type[2] { typeof(RectTransform), typeof(Image) }); val3.transform.SetParent(val.transform, false); val3.GetComponent<Image>().sprite = KitArt.Button; ((Graphic)val3.GetComponent<Image>()).raycastTarget = false; picker.marker = (RectTransform)val3.transform; picker.marker.sizeDelta = new Vector2(10f, 10f); KitAppearance.Changed += picker.Restyle; string[] array = new string[4] { "R", "G", "B", "A" }; for (int i = 0; i < 4; i++) { int component = i; KitAlmanacDialog.Label((Transform)(object)dialog.Panel, "Label" + i, array[i], new Vector2(280f, (float)(-60 - i * 72)), new Vector2(24f, 28f)); picker.components[i] = KitAlmanacNumber.Create(dialog.Panel, "Component" + i, new Vector2(310f, (float)(-60 - i * 72)), new Vector2(280f, 32f), "0", Valid, delegate(string text) { if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { ((Color)(ref picker.value))[component] = Mathf.Clamp01(result); picker.Follow(convert: true); } }, (int direction) => (((Color)(ref picker.value))[component] + (float)direction * 0.01f).ToString("R", CultureInfo.InvariantCulture), 0.0, 1.0, 0.01); dialog.Own(picker.components[i].Dispose); dialog.Window.Focusable((Selectable)(object)picker.components[i].Field.Input, 1, i); } picker.hueField = KitAlmanacNumber.Create(dialog.Panel, "Hue", new Vector2(310f, -348f), new Vector2(280f, 65f), "0", Valid, delegate(string text) { if (float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { picker.hue = Mathf.Repeat(result / 360f, 1f); picker.FromHsv(); } }, (int direction) => (picker.hue * 360f + (float)direction).ToString("R", CultureInfo.InvariantCulture), 0.0, 360.0); dialog.Own(picker.hueField.Dispose); picker.MakeHueStrip(); picker.hex = KitTextField.Create(dialog.Panel, "Hex", (KitFieldMode)2, new Vector2(310f, -425f), new Vector2(280f, 32f)); Color val4 = default(Color); picker.hex.Validator = (string text) => ColorUtility.TryParseHtmlString("#" + text, ref val4); picker.hex.Ended += delegate(KitFieldEdit edit) { //IL_0029: 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) if (((KitFieldEdit)(ref edit)).Commit && ColorUtility.TryParseHtmlString("#" + ((KitFieldEdit)(ref edit)).Text, ref val4)) { picker.value = val4; picker.Follow(convert: true); } }; dialog.Own(picker.hex.Destroy); Button control = KitButton.Create(dialog.Panel, "Apply", "Apply", dialog.Dispose, new Vector2(400f, -530f), new Vector2(90f, 30f)); Button control2 = KitButton.Create(dialog.Panel, "Cancel", "Cancel", delegate { picker.cancelled = true; dialog.Dispose(); }, new Vector2(500f, -530f), new Vector2(90f, 30f)); dialog.Window.Focusable((Selectable)(object)control, 0, 5); dialog.Window.Focusable((Selectable)(object)control2, 1, 5); dialog.Closed += delegate { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!picker.cancelled) { picker.commit(picker.value); } }; picker.Follow(convert: true); } private static bool Valid(string value) { if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && !float.IsNaN(result)) { return !float.IsInfinity(result); } return false; } private void MakeHueStrip() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_005c: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) Slider slider = hueField.Slider; if ((Object)(object)slider == (Object)null) { return; } slider.direction = (Direction)0; hueTexture = new Texture2D(256, 1, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; for (int i = 0; i < 256; i++) { hueTexture.SetPixel(i, 0, Color.HSVToRGB((float)i / 255f, 1f, 1f)); } hueTexture.Apply(); GameObject val = new GameObject("HueGradient", new Type[2] { typeof(RectTransform), typeof(RawImage) }) { layer = ((Component)slider).gameObject.layer }; val.transform.SetParent(((Component)slider).transform, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; Vector2 offsetMin = (val2.offsetMax = Vector2.zero); val2.offsetMin = offsetMin; RawImage component = val.GetComponent<RawImage>(); component.texture = (Texture)(object)hueTexture; ((Graphic)component).raycastTarget = false; if ((Object)(object)slider.handleRect != (Object)null) { Transform val3 = (Transform)(object)slider.handleRect; while ((Object)(object)val3.parent != (Object)null && (Object)(object)val3.parent != (Object)(object)((Component)slider).transform) { val3 = val3.parent; } val3.SetAsLastSibling(); } } private void Follow(bool convert) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) if (changing) { return; } changing = true; if (convert) { Color.RGBToHSV(value, ref hue, ref saturation, ref brightness); } if ((Object)(object)texture == (Obj
plugins/Crucible/Crucible.dll
Decompiled 10 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.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Entities; using Jotunn.Extensions; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExtraPotions")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (C) 2026 expDARE (ExtraPotions Meadworks)")] [assembly: AssemblyFileVersion("0.3.34.0")] [assembly: AssemblyInformationalVersion("0.3.34+fatal: not a git repository (or any of the parent directories): .git")] [assembly: AssemblyProduct("Crucible")] [assembly: AssemblyTitle("Crucible")] [assembly: AssemblyVersion("0.3.34.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 ExtraPotions.Meadworks.Crucible { public sealed class ConfigDependency { public Func<bool> Enabled { get; } public Func<bool>? Visible { get; } public Func<string> Reason { get; } public ConfigDependency(Func<bool> enabled, Func<string> reason, Func<bool>? visible = null) { Enabled = enabled ?? throw new ArgumentNullException("enabled"); Reason = reason ?? throw new ArgumentNullException("reason"); Visible = visible; } } internal sealed class PresetPublicationException : IOException { internal PresetPublicationException(Exception error) : base("Preset was published, but final readback failed.", error) { } } public static class MeadworksConfig { private sealed class ManagedEntry { internal bool Bound; internal bool Tracked; internal string BoundProduct = ""; internal string Product = ""; internal ConfigScope BoundScope; internal ConfigScope Scope; internal object? Safe; internal bool ServerLocked = true; internal ConfigVisibility Visibility; } private enum EditStep { Authority, Assignment, Serialize, ReadOriginal, StageCopy, Parse, Bind, Save, Flush, Reload, ReadStaged, ConflictRead, Publish, Readback, Cleanup, Count } private sealed class EditTimingData { internal readonly long Start = Stopwatch.GetTimestamp(); internal readonly long[] Steps = new long[15]; internal readonly int Thread = Environment.CurrentManagedThreadId; internal readonly int Gc0 = GC.CollectionCount(0); internal readonly int Gc1 = GC.CollectionCount(1); internal readonly int Gc2 = GC.CollectionCount(2); internal int Frame = -1; internal int Bytes; internal int Persists; internal int Amendments; internal string Operation; internal string Product = "unknown"; internal string Route = "unresolved"; internal bool Complete; internal EditTimingData(string operation) { Operation = operation; CaptureEditFrame(ref Frame); } internal string Line(long end, int gc0, int gc1, int gc2) { StringBuilder stringBuilder = new StringBuilder("stall-timing config-edit operation=").Append(Operation).Append(" product=").Append(Product) .Append(" route=") .Append(Route) .Append(" status=") .Append(Complete ? "completed" : "exception") .Append(" frame=") .Append(Frame) .Append(" thread=") .Append(Thread) .Append(" start_ticks=") .Append(Start) .Append(" end_ticks=") .Append(end) .Append(" frequency=") .Append(Stopwatch.Frequency) .Append(" total_ms=") .Append(((double)(end - Start) * 1000.0 / (double)Stopwatch.Frequency).ToString("F3", CultureInfo.InvariantCulture)) .Append(" bytes=") .Append(Bytes) .Append(" persists=") .Append(Persists) .Append(" amendments=") .Append(Amendments) .Append(" gc=") .Append(gc0 - Gc0) .Append('/') .Append(gc1 - Gc1) .Append('/') .Append(gc2 - Gc2); for (int i = 0; i < Steps.Length; i++) { stringBuilder.Append(' ').Append((EditStep)i).Append("_ticks=") .Append(Steps[i]); } return stringBuilder.ToString(); } } private readonly struct EditSpan : IDisposable { private readonly EditTimingData? data; private readonly EditStep step; private readonly long start; internal EditSpan(EditTimingData? data, EditStep step) { this.data = data; this.step = step; start = ((data == null) ? 0 : Stopwatch.GetTimestamp()); } public void Dispose() { if (data != null) { data.Steps[(int)step] += Stopwatch.GetTimestamp() - start; } } } private readonly struct EditTiming : IDisposable { private readonly EditTimingData? data; private readonly bool owner; private EditTiming(EditTimingData? data, bool owner) { this.data = data; this.owner = owner; } internal static EditTiming Begin(string operation) { if (activeEditTiming != null) { return new EditTiming(activeEditTiming, owner: false); } bool enabled = false; EnableEditTiming(ref enabled); if (!enabled) { return default(EditTiming); } return new EditTiming(activeEditTiming = new EditTimingData(operation), owner: true); } internal EditSpan Step(EditStep step) { return new EditSpan(data, step); } internal unsafe void Describe(string product, AmendRoute route) { if (data != null && data.Amendments++ == 0) { data.Product = product; data.Route = ((object)(*(AmendRoute*)(&route))/*cast due to .constrained prefix*/).ToString(); } } internal void FileBytes(int bytes) { if (data != null) { data.Bytes += bytes; data.Persists++; } } internal void Success() { if (owner && data != null) { data.Complete = true; } } public void Dispose() { if (!owner || data == null) { return; } long timestamp = Stopwatch.GetTimestamp(); int gc = GC.CollectionCount(0); int gc2 = GC.CollectionCount(1); int gc3 = GC.CollectionCount(2); activeEditTiming = null; try { WriteEditTiming(data.Line(timestamp, gc, gc2, gc3)); } catch { } } } private sealed class OwnValueStagingMarker { } private sealed class OwnValueStagingConstruction { private int complete; internal bool Pending => Volatile.Read(in complete) == 0; internal void Complete() { Volatile.Write(ref complete, 1); } } private static readonly ConditionalWeakTable<ConfigEntryBase, ManagedEntry> managedEntries = new ConditionalWeakTable<ConfigEntryBase, ManagedEntry>(); private static readonly MethodInfo TypedAmend = typeof(MeadworksConfig).GetMethods(BindingFlags.Static | BindingFlags.Public).Single((MethodInfo method) => method.Name == "Amend" && method.IsGenericMethodDefinition && method.GetGenericArguments().Length == 1 && method.GetParameters().Length == 6); [ThreadStatic] private static EditTimingData? activeEditTiming; private static readonly ConditionalWeakTable<ConfigFile, OwnValueStagingMarker> ownValueStaging = new ConditionalWeakTable<ConfigFile, OwnValueStagingMarker>(); private static readonly OwnValueStagingMarker ownValueStagingMarker = new OwnValueStagingMarker(); [ThreadStatic] private static OwnValueStagingConstruction? ownValueStagingConstruction; public static object? CurrentOwnValueStagingConstruction => ownValueStagingConstruction; internal static void PersistPresetEntries(IReadOnlyList<ConfigEntryBase> entries) { //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Expected O, but got Unknown if (entries == null || entries.Count == 0) { throw new ArgumentException("A preset needs entries.", "entries"); } ConfigFile configFile = entries[0].ConfigFile; HashSet<ConfigEntryBase> hashSet = new HashSet<ConfigEntryBase>(); List<(ConfigEntryBase, object, string)> list = new List<(ConfigEntryBase, object, string)>(); foreach (ConfigEntryBase entry in entries) { if (entry == null || entry.ConfigFile != configFile || !hashSet.Add(entry) || !configFile.ContainsKey(entry.Definition) || configFile[entry.Definition] != entry) { throw new ArgumentException("Preset entries must be distinct current entries from one file.", "entries"); } list.Add((entry, entry.BoxedValue, TomlTypeConverter.ConvertToString(entry.BoxedValue, entry.SettingType))); } string fullPath = Path.GetFullPath(configFile.ConfigFilePath); string text = fullPath + ".preset-" + Guid.NewGuid().ToString("N") + ".tmp"; byte[] array = (File.Exists(fullPath) ? File.ReadAllBytes(fullPath) : null); bool flag = false; bool flag2 = false; try { Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); using (FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { flag = true; if (array != null) { fileStream.Write(array, 0, array.Length); } } ConfigFile val = CreateOwnValueStaging(text); MethodInfo methodInfo = typeof(ConfigFile).GetMethods(BindingFlags.Instance | BindingFlags.Public).Single((MethodInfo method) => method.Name == "Bind" && method.IsGenericMethodDefinition && method.GetParameters().Length == 3 && method.GetParameters()[0].ParameterType == typeof(ConfigDefinition)); List<(ConfigEntryBase, string)> list2 = new List<(ConfigEntryBase, string)>(); foreach (var item2 in list) { ConfigEntryBase item = item2.Item1; ConfigEntryBase val2 = (ConfigEntryBase)methodInfo.MakeGenericMethod(item.SettingType).Invoke(val, new object[3] { item.Definition, item.DefaultValue, item.Description }); val2.BoxedValue = item2.Item2; if (TomlTypeConverter.ConvertToString(val2.BoxedValue, val2.SettingType) != item2.Item3) { throw new IOException("The configuration serializer changed a preset value."); } list2.Add((val2, item2.Item3)); } val.Save(); using (FileStream fileStream2 = new FileStream(text, FileMode.Open, FileAccess.Write, FileShare.None)) { fileStream2.Flush(flushToDisk: true); } val.Reload(); foreach (var item3 in list2) { if (TomlTypeConverter.ConvertToString(item3.Item1.BoxedValue, item3.Item1.SettingType) != item3.Item2) { throw new IOException("A saved preset value failed readback."); } } byte[] array2 = File.ReadAllBytes(text); bool num; if (array != null) { if (!File.Exists(fullPath)) { goto IL_033c; } num = !((ReadOnlySpan<byte>)array).SequenceEqual((ReadOnlySpan<byte>)File.ReadAllBytes(fullPath)); } else { num = File.Exists(fullPath); } if (!num) { if (array == null) { File.Move(text, fullPath); } else { File.Replace(text, fullPath, null); } flag = false; flag2 = true; if (!((ReadOnlySpan<byte>)array2).SequenceEqual((ReadOnlySpan<byte>)File.ReadAllBytes(fullPath))) { throw new IOException("The published preset configuration failed readback."); } return; } goto IL_033c; IL_033c: throw new IOException("The configuration changed on disk during the preset edit."); } catch (Exception error) when (flag2) { throw new PresetPublicationException(error); } finally { if (flag && File.Exists(text)) { File.Delete(text); } } } public static ConfigAmendResult AmendBatch(string productId, IReadOnlyList<ConfigEntryBase> entries, Action apply, string? label = null) { //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0134: 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_019c: Invalid comparison between Unknown and I4 //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(productId)) { throw new ArgumentException("A product owner is required.", "productId"); } if (entries == null) { throw new ArgumentNullException("entries"); } if (apply == null) { throw new ArgumentNullException("apply"); } if (entries.Count == 0) { throw new ArgumentException("An amendment batch needs at least one entry.", "entries"); } HashSet<ConfigEntryBase> hashSet = new HashSet<ConfigEntryBase>(); List<AuthorityDecision> list = new List<AuthorityDecision>(entries.Count); foreach (ConfigEntryBase entry in entries) { if (entry == null || !hashSet.Add(entry)) { throw new ArgumentException("A batch needs distinct non-null entries.", "entries"); } if (!TryGetMetadata(entry, out ConfigEntryMetadata metadata) || !string.Equals(metadata.ProductId, productId, StringComparison.Ordinal)) { throw new InvalidOperationException("The batch contains an entry without the exact registered product owner."); } list.Add(metadata.Decision); } SessionRole val = (SessionRole)(Plugin.IsReady ? ((int)Plugin.Session.Role) : 0); AmendRoute val2 = ConfigAmendment.BatchRoute(val, (IReadOnlyList<AuthorityDecision>)list); if ((int)val2 == 0) { for (int i = 0; i < list.Count; i++) { if ((int)ConfigAmendment.Route(val, list[i]) == 0) { MeadworksBlockedOptions.Notify(BlockedOption.From(productId, entries[i].Definition.Key, list[i]), label); return new ConfigAmendResult(val2, list[i], applied: false); } } } AuthorityDecision decision = list[0]; foreach (AuthorityDecision item in list) { if (ConfigAmendment.Route(val, item) == val2) { decision = item; break; } } if ((int)val2 == 1) { apply(); return new ConfigAmendResult(val2, decision, applied: true); } if (!JotunnConfigBridge.Available) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Meadworks:Crucible] Configuration batch not applied: Jotunn synchronization is unavailable."); } return new ConfigAmendResult(val2, decision, applied: false); } return new ConfigAmendResult(val2, decision, JotunnConfigBridge.Publish(apply)); } private static object? ClauseMember(object target, string name) { Type obj = (target as Type) ?? target.GetType(); object obj2 = ((target is Type) ? null : target); Type type = obj; while (type != null) { PropertyInfo property = type.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(obj2); } FieldInfo field = type.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(obj2); } type = type.BaseType; } return null; } public static bool TryGetRegisteredClause(ConfigEntryBase entry, out ConfigClauseRegistration registration) { registration = null; if (entry == null || !entry.ConfigFile.ContainsKey(entry.Definition) || entry.ConfigFile[entry.Definition] != entry) { return false; } try { object obj = null; ConfigClauseRegistration configClauseRegistration = null; string[] array = new string[2] { "Burrow", "Mantle" }; foreach (string text in array) { string text2 = "com.meadworks." + text.ToLowerInvariant(); if (!Chainloader.PluginInfos.TryGetValue(text2, out var value) || (Object)(object)value.Instance == (Object)null || value.Instance.Config != entry.ConfigFile) { continue; } Assembly assembly = ((object)value.Instance).GetType().Assembly; string text3 = "ExtraPotions." + text + ".Embedded.Charter"; Type type = assembly.GetType(text3 + ".GameHooks", throwOnError: false); if (type == null || !(ClauseMember(type, "charters") is IEnumerable enumerable)) { continue; } object obj2 = ClauseMember(((object)value.Instance).GetType(), "Synced"); if (obj2 == null || ClauseMember(obj2, "Config") != entry.ConfigFile) { continue; } object obj3 = ClauseMember(obj2, "Sync"); if (obj3 == null) { continue; } foreach (object item in enumerable) { if (item == null || item != obj3 || item.GetType().Assembly != assembly || item.GetType().FullName != text3 + ".Charter" || !(ClauseMember(item, "Guid") is string text4) || text4 != text2) { continue; } object obj4 = ClauseMember(item, "Ledger"); if (obj4 == null || !(ClauseMember(obj4, "Clauses") is IEnumerable enumerable2)) { continue; } foreach (object item2 in enumerable2) { if (item2 == null || ClauseMember(item2, "Entry") != entry) { continue; } Type type2 = item2.GetType(); if (!(type2.Assembly != assembly) && type2.IsGenericType && !(type2.GetGenericTypeDefinition().FullName != text3 + ".Clause`1") && !(type2.GetGenericArguments()[0] != entry.SettingType) && ClauseMember(item2, "Local") is bool local) { object obj5 = ClauseMember(item2, "OwnValue"); if (obj5 != null && !(obj5.GetType() != entry.SettingType) && ClauseMember(item, "IsBound") is bool bound && ClauseMember(item, "MayAmend") is bool mayAmend) { if (obj != null && obj != item2) { return false; } obj = item2; configClauseRegistration = new ConfigClauseRegistration(text4, local, obj5, bound, mayAmend); continue; } } return false; } } } if (configClauseRegistration == null) { return false; } registration = configClauseRegistration; return true; } catch { return false; } } public static ConfigEntry<T> Bind<T>(ConfigFile config, string section, string key, T defaultValue, string description, ConfigScope scope = (ConfigScope)0, AcceptableValueBase? acceptableValues = null) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (!Enum.IsDefined(typeof(ConfigScope), scope)) { throw new ArgumentException("Unknown configuration scope."); } ConfigEntry<T> obj = ConfigFileExtensions.BindConfig<T>(config, section, key, defaultValue, description, (int)scope > 0, (int?)null, acceptableValues, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); RecordBinding((ConfigEntryBase)(object)obj, MeadworksMischief.OwnerOf(config), scope, defaultValue, (ConfigVisibility)0); return obj; } public static ConfigEntry<T> BindHidden<T>(ConfigFile config, string section, string key, T defaultValue, string description, ConfigScope scope = (ConfigScope)0, AcceptableValueBase? acceptableValues = null) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_004e: Unknown result type (might be due to invalid IL or missing references) ConfigVisibilityRules.Require(scope, (ConfigVisibility)1); ConfigurationManagerAttributes val = new ConfigurationManagerAttributes { Browsable = ConfigVisibilityRules.Browsable((ConfigVisibility)1), ReadOnly = ConfigVisibilityRules.ReadOnly((ConfigVisibility)1) }; bool num = (int)scope > 0; ConfigurationManagerAttributes val2 = val; ConfigEntry<T> obj = ConfigFileExtensions.BindConfig<T>(config, section, key, defaultValue, description, num, (int?)null, acceptableValues, (Action<ConfigEntryBase>)null, val2); RecordBinding((ConfigEntryBase)(object)obj, MeadworksMischief.OwnerOf(config), scope, defaultValue, (ConfigVisibility)1); return obj; } public static ConfigEntry<T> BindCheat<T>(ConfigFile config, string section, string key, T safeValue, string description, AcceptableValueBase? acceptableValues = null) { ConfigEntry<T> result = Bind(config, section, key, safeValue, description, (ConfigScope)2, acceptableValues); MeadworksMischief.Cheats.Tag(MeadworksMischief.OwnerOf(config), section + "." + key, (MischiefCategory?)null); return result; } public static ConfigEntry<T> BindCheat<T>(ConfigFile config, string section, string key, T safeValue, string description, MischiefCategory category, AcceptableValueBase? acceptableValues = null) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<T> result = Bind(config, section, key, safeValue, description, (ConfigScope)2, acceptableValues); MeadworksMischief.Cheats.Tag(MeadworksMischief.OwnerOf(config), section + "." + key, (MischiefCategory?)category); return result; } public static bool CanEdit(ConfigScope scope) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (Plugin.IsReady) { return ConfigAuthority.CanEdit(scope, Plugin.Session); } return false; } public static ConfigEditDecision EditDecision(ConfigScope scope, bool serverLocked = true, ServerCrucible? server = null) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsReady) { return ConfigEditPolicy.Decide(scope, new ConfigAuthorityFacts((SessionRole)3, false, false, (ServerCrucible)0), serverLocked); } SessionSnapshot session = Plugin.Session; return ConfigEditPolicy.Decide(scope, ConfigAuthorityFacts.From(session, (ServerCrucible)(((??)server) ?? MeadworksAuthority.Server)), serverLocked); } public static T Effective<T>(ConfigEntry<T> entry, string productId, ConfigScope scope, T safe, bool serverLocked = true) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (entry == null) { throw new ArgumentNullException("entry"); } return ConfigValues.Resolve<T>(MeadworksAuthority.Decide(productId, scope, serverLocked), entry.Value, safe); } public static ConfigAmendResult Amend<T>(ConfigEntry<T> entry, string productId, ConfigScope scope, T value, string? label = null, bool serverLocked = true) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected I4, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) using EditTiming editTiming = EditTiming.Begin("Amend<T>"); if (entry == null) { throw new ArgumentNullException("entry"); } AuthorityDecision val; AmendRoute val2; using (editTiming.Step(EditStep.Authority)) { val = MeadworksAuthority.Decide(productId, scope, serverLocked); val2 = ConfigAmendment.Route((SessionRole)(Plugin.IsReady ? ((int)Plugin.Session.Role) : 0), val); } editTiming.Describe(productId, val2); ConfigAmendResult result; switch ((int)val2) { case 0: MeadworksBlockedOptions.Notify(BlockedOption.From(productId, ((ConfigEntryBase)entry).Definition.Key, val), label); result = new ConfigAmendResult(val2, val, applied: false); break; case 1: AssignOwn(entry, value); result = new ConfigAmendResult(val2, val, applied: true); break; case 2: result = new ConfigAmendResult(val2, val, JotunnConfigBridge.Publish(delegate { AssignOwn(entry, value); })); break; default: result = new ConfigAmendResult(val2, val, JotunnConfigBridge.Publish(delegate { entry.Value = value; })); break; } editTiming.Success(); return result; } public static int EnsureSchema(ConfigFile config, int target, IReadOnlyList<MigrationStep>? steps = null) { //IL_0045: 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_0057: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; try { ConfigEntry<int> schema = config.Bind<int>("Meadworks", "SchemaVersion", 0, new ConfigDescription("Configuration schema; managed by Meadworks.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { Browsable = false } })); steps = (IReadOnlyList<MigrationStep>?)(((object)steps) ?? ((object)new MigrationStep[1] { new MigrationStep(0, 1, (Action)delegate { }) })); ConfigMigration.Run(schema.Value, target, steps, (Action)delegate { if (File.Exists(config.ConfigFilePath)) { File.Copy(config.ConfigFilePath, config.ConfigFilePath + ".schema-" + schema.Value + "." + DateTime.UtcNow.Ticks + ".bak", overwrite: false); } }, (Action<int>)delegate(int v) { schema.Value = v; config.Save(); }); return schema.Value; } finally { config.SaveOnConfigSet = saveOnConfigSet; } } public static ConfigEntry<T> Bind<T>(ConfigFile config, string section, string key, T defaultValue, ConfigDescription description) { return Bind(config, section, key, defaultValue, description, (ConfigScope)0); } public static ConfigEntry<T> Bind<T>(ConfigFile config, string section, string key, T defaultValue, ConfigDescription description, ConfigScope scope) { //IL_0019: 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_0056: Invalid comparison between Unknown and I4 //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (description == null) { throw new ArgumentNullException("description"); } if (!Enum.IsDefined(typeof(ConfigScope), scope)) { throw new ArgumentException("Unknown configuration scope.", "scope"); } object[] array = description.Tags ?? Array.Empty<object>(); object[] array2 = new object[array.Length + 1]; bool flag = (int)scope > 0; array2[0] = (object)new ConfigurationManagerAttributes { IsAdminOnly = flag }; Array.Copy(array, 0, array2, 1, array.Length); ConfigEntry<T> obj = config.Bind<T>(section, key, defaultValue, new ConfigDescription(description.Description + (flag ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, array2)); RecordBinding((ConfigEntryBase)(object)obj, MeadworksMischief.OwnerOf(config), scope, defaultValue, (ConfigVisibility)0); return obj; } internal static void RecordBinding(ConfigEntryBase entry, string product, ConfigScope scope, object? safe, ConfigVisibility visibility = (ConfigVisibility)0) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) ManagedEntry value = managedEntries.GetValue(entry, (ConfigEntryBase _) => new ManagedEntry()); value.Bound = true; value.BoundProduct = product; value.BoundScope = scope; value.Safe = safe; value.Visibility = visibility; if (!value.Tracked) { value.Product = product; value.Scope = scope; value.ServerLocked = true; } if ((int)scope != 0) { SyncedConfigGuard.Watch(entry); } } internal static void RecordTracking(ConfigEntryBase entry, string product, ConfigScope scope, bool serverLocked) { //IL_001f: 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) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (!string.IsNullOrWhiteSpace(product) && Enum.IsDefined(typeof(ConfigScope), scope)) { ManagedEntry value = managedEntries.GetValue(entry, (ConfigEntryBase _) => new ManagedEntry { Safe = entry.DefaultValue }); value.Tracked = true; value.Product = product; value.Scope = scope; value.ServerLocked = serverLocked; } } internal static void EndTracking(ConfigEntryBase entry) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (entry != null && managedEntries.TryGetValue(entry, out ManagedEntry value)) { if (!value.Bound) { managedEntries.Remove(entry); return; } value.Tracked = false; value.Product = value.BoundProduct; value.Scope = value.BoundScope; value.ServerLocked = true; } } public static bool TryGetMetadata(ConfigEntryBase entry, out ConfigEntryMetadata metadata) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if (entry != null && managedEntries.TryGetValue(entry, out ManagedEntry value) && !string.IsNullOrWhiteSpace(value.Product)) { metadata = new ConfigEntryMetadata(value.Product, value.Scope, value.Safe, value.ServerLocked, value.Visibility); return true; } if (TryGetRegisteredClause(entry, out ConfigClauseRegistration registration) && (int)registration.Scope == 0) { ConfigVisibility visibility = (ConfigVisibility)0; try { object[] tags = entry.Description.Tags; foreach (object obj in tags) { if (obj != null) { object obj2 = ClauseMember(obj, "Browsable"); if (obj2 is bool && !(bool)obj2) { visibility = (ConfigVisibility)1; } } } } catch { metadata = null; return false; } metadata = new ConfigEntryMetadata(registration.ProductId, (ConfigScope)0, entry.DefaultValue, locked: true, visibility); return true; } metadata = null; return false; } public static ConfigAmendResult Amend(ConfigEntryBase entry, object? value, string? label = null) { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) using EditTiming editTiming = EditTiming.Begin("Amend"); if (entry == null) { throw new ArgumentNullException("entry"); } if (!TryGetMetadata(entry, out ConfigEntryMetadata metadata)) { throw new InvalidOperationException("No registered Meadworks owner for this configuration entry."); } Type settingType = entry.SettingType; if (((object)entry).GetType() != typeof(ConfigEntry<>).MakeGenericType(settingType)) { throw new ArgumentException("Expected the exact registered BepInEx configuration entry type.", "entry"); } bool num; if (value != null) { num = !settingType.IsInstanceOfType(value); } else { if (!settingType.IsValueType) { goto IL_00a4; } num = Nullable.GetUnderlyingType(settingType) == null; } if (num) { throw new ArgumentException("The value must have the entry's exact managed setting type.", "value"); } goto IL_00a4; IL_00a4: try { ConfigAmendResult result = (ConfigAmendResult)TypedAmend.MakeGenericMethod(settingType).Invoke(null, new object[6] { entry, metadata.ProductId, metadata.Scope, value, label, metadata.ServerLocked }); editTiming.Success(); return result; } catch (TargetInvocationException ex) when (ex.InnerException != null) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); throw; } } private static void EnableEditTiming(ref bool enabled) { enabled = Plugin.Log != null && MeadworksLogging.Enabled("Crucible", (LogSeverity)4); } private static void CaptureEditFrame(ref int frame) { frame = Time.frameCount; } private static void WriteEditTiming(string line) { if (MeadworksLogging.Enabled("Crucible", (LogSeverity)4)) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogDebug((object)("[Meadworks:Crucible] " + line)); } } } public static void PersistOwnValue(ConfigEntryBase entry) { //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Expected O, but got Unknown using EditTiming editTiming = EditTiming.Begin("PersistOwnValue"); if (entry == null) { throw new ArgumentNullException("entry"); } ConfigFile configFile = entry.ConfigFile; if (!configFile.ContainsKey(entry.Definition) || configFile[entry.Definition] != entry) { throw new InvalidOperationException("The configuration entry is no longer bound to its file."); } string text; using (editTiming.Step(EditStep.Serialize)) { text = TomlTypeConverter.ConvertToString(entry.BoxedValue, entry.SettingType); } string fullPath = Path.GetFullPath(configFile.ConfigFilePath); string text2 = fullPath + ".edit-" + Guid.NewGuid().ToString("N") + ".tmp"; byte[] array; using (editTiming.Step(EditStep.ReadOriginal)) { array = (File.Exists(fullPath) ? File.ReadAllBytes(fullPath) : null); } editTiming.FileBytes((array != null) ? array.Length : 0); bool flag = false; try { using (editTiming.Step(EditStep.StageCopy)) { Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); using FileStream fileStream = new FileStream(text2, FileMode.CreateNew, FileAccess.Write, FileShare.None); flag = true; if (array != null) { fileStream.Write(array, 0, array.Length); } } ConfigFile val; using (editTiming.Step(EditStep.Parse)) { val = CreateOwnValueStaging(text2); } ConfigEntryBase val2; using (editTiming.Step(EditStep.Bind)) { val2 = (ConfigEntryBase)typeof(ConfigFile).GetMethods(BindingFlags.Instance | BindingFlags.Public).Single((MethodInfo method) => method.Name == "Bind" && method.IsGenericMethodDefinition && method.GetParameters().Length == 3 && method.GetParameters()[0].ParameterType == typeof(ConfigDefinition)).MakeGenericMethod(entry.SettingType) .Invoke(val, new object[3] { entry.Definition, entry.DefaultValue, entry.Description }); val2.BoxedValue = entry.BoxedValue; if (TomlTypeConverter.ConvertToString(val2.BoxedValue, val2.SettingType) != text) { throw new IOException("The configuration serializer changed the requested value."); } } using (editTiming.Step(EditStep.Save)) { val.Save(); } using (editTiming.Step(EditStep.Flush)) { using FileStream fileStream2 = new FileStream(text2, FileMode.Open, FileAccess.Write, FileShare.None); fileStream2.Flush(flushToDisk: true); } using (editTiming.Step(EditStep.Reload)) { if (!ConfigValueReadback.TryReload(val2, text2)) { val.Reload(); } if (TomlTypeConverter.ConvertToString(val2.BoxedValue, val2.SettingType) != text) { throw new IOException("The saved configuration value failed readback."); } } byte[] array2; using (editTiming.Step(EditStep.ReadStaged)) { array2 = File.ReadAllBytes(text2); } using (editTiming.Step(EditStep.ConflictRead)) { bool num; if (array != null) { if (!File.Exists(fullPath)) { goto IL_0332; } num = !((ReadOnlySpan<byte>)array).SequenceEqual((ReadOnlySpan<byte>)File.ReadAllBytes(fullPath)); } else { num = File.Exists(fullPath); } if (num) { goto IL_0332; } goto end_IL_02ff; IL_0332: throw new IOException("The configuration changed on disk during the edit."); end_IL_02ff:; } using (editTiming.Step(EditStep.Publish)) { try { if (array == null) { File.Move(text2, fullPath); } else { File.Replace(text2, fullPath, null); } } catch (IOException ex) { throw new IOException("Configuration atomic " + ((array == null) ? "creation" : "replacement") + " failed (HRESULT 0x" + ex.HResult.ToString("X8") + "): " + ex.Message, ex); } } flag = false; using (editTiming.Step(EditStep.Readback)) { if (!((ReadOnlySpan<byte>)array2).SequenceEqual((ReadOnlySpan<byte>)File.ReadAllBytes(fullPath))) { throw new IOException("The published configuration failed readback."); } } } finally { using (editTiming.Step(EditStep.Cleanup)) { if (flag && File.Exists(text2)) { File.Delete(text2); } } } editTiming.Success(); } private static void AssignOwn<T>(ConfigEntry<T> entry, T value) { using EditTiming editTiming = EditTiming.Begin("AssignOwn"); ConfigFile configFile = ((ConfigEntryBase)entry).ConfigFile; bool saveOnConfigSet = configFile.SaveOnConfigSet; T value2 = entry.Value; configFile.SaveOnConfigSet = false; try { using (editTiming.Step(EditStep.Assignment)) { entry.Value = value; } if (TomlTypeConverter.ConvertToString((object)entry.Value, typeof(T)) != TomlTypeConverter.ConvertToString((object)value, typeof(T))) { throw new InvalidOperationException("The mod did not retain the requested setting."); } PersistOwnValue((ConfigEntryBase)(object)entry); } catch { entry.Value = value2; throw; } finally { configFile.SaveOnConfigSet = saveOnConfigSet; } editTiming.Success(); } public static ConfigPresentationText Presentation(ConfigEntryBase entry, string language, string label, string section, string help) { ConfigPresentationText configPresentationText = new ConfigPresentationText(label, section, help); if (entry == null || !entry.ConfigFile.ContainsKey(entry.Definition) || entry.ConfigFile[entry.Definition] != entry) { return configPresentationText; } IConfigPresentation configPresentation = null; object[] array = entry.Description.Tags ?? Array.Empty<object>(); for (int i = 0; i < array.Length; i++) { if (array[i] is IConfigPresentation configPresentation2) { if (configPresentation != null) { return configPresentationText; } configPresentation = configPresentation2; } } if (configPresentation == null) { return Conventional(entry, language, configPresentationText); } if (!configPresentation.Supports(entry) || !TryPresentationOwner(entry, out string owner) || !string.Equals(configPresentation.ProductId, owner, StringComparison.Ordinal)) { return configPresentationText; } try { ConfigPresentationText configPresentationText2 = configPresentation.Resolve(entry, language ?? "English"); if (configPresentationText2 == null) { return configPresentationText; } return new ConfigPresentationText(Prefer(configPresentationText2.Label, configPresentationText.Label), Prefer(configPresentationText2.Section, configPresentationText.Section), Prefer(configPresentationText2.Help, configPresentationText.Help)); } catch { return configPresentationText; } } private static string Prefer(string value, string fallback) { if (!string.IsNullOrWhiteSpace(value)) { return value; } return fallback; } private static bool TryPresentationOwner(ConfigEntryBase entry, out string owner) { if (TryGetMetadata(entry, out ConfigEntryMetadata metadata)) { owner = metadata.ProductId; return true; } owner = Path.GetFileNameWithoutExtension(entry.ConfigFile.ConfigFilePath) ?? ""; if (owner.StartsWith("com.meadworks.", StringComparison.Ordinal)) { return owner.Length > "com.meadworks.".Length; } return false; } private static ConfigPresentationText Conventional(ConfigEntryBase entry, string language, ConfigPresentationText fallback) { if (!TryPresentationOwner(entry, out string owner) || !SettingTranslations.TryGet(owner, out ProductTranslations translations)) { return fallback; } string section = entry.Definition.Section; string key = entry.Definition.Key; language = language ?? "English"; return new ConfigPresentationText(Lookup(SettingTextKeys.Name(section, key), fallback.Label), Lookup(SettingTextKeys.Heading(section), fallback.Section), Lookup(SettingTextKeys.Help(section, key), fallback.Help)); string Lookup(string? textKey, string raw) { if (textKey == null) { return raw; } string text = translations.Text(textKey, language); if (!string.Equals(text, textKey, StringComparison.Ordinal)) { return Prefer(text, raw); } return raw; } } public static bool IsOwnValueStaging(ConfigFile? file) { OwnValueStagingMarker value; if (file != null) { return ownValueStaging.TryGetValue(file, out value); } return false; } public static bool IsOwnValueStagingConstructionPending(object? construction) { if (construction is OwnValueStagingConstruction ownValueStagingConstruction) { return ownValueStagingConstruction.Pending; } return false; } private static ConfigFile CreateOwnValueStaging(string path) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown OwnValueStagingConstruction ownValueStagingConstruction = MeadworksConfig.ownValueStagingConstruction; OwnValueStagingConstruction ownValueStagingConstruction2 = (MeadworksConfig.ownValueStagingConstruction = new OwnValueStagingConstruction()); try { ConfigFile val = new ConfigFile(path, false) { SaveOnConfigSet = false }; ownValueStaging.Add(val, ownValueStagingMarker); return val; } finally { ownValueStagingConstruction2.Complete(); MeadworksConfig.ownValueStagingConstruction = ownValueStagingConstruction; } } internal static bool ServerBound(ConfigEntryBase entry, out string product) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 product = ""; if (entry == null || !managedEntries.TryGetValue(entry, out ManagedEntry value) || !value.Bound) { return false; } product = value.Product; if ((int)value.Scope != 2) { if ((int)value.Scope == 1) { return value.ServerLocked; } return false; } return true; } } public enum ConfigPresetFailure { Invalid, Stale, Locked, HostOnly, Write } public sealed class ConfigPresetException : InvalidOperationException { public ConfigPresetFailure Failure { get; } internal ConfigPresetException(ConfigPresetFailure failure, string detail) : base(detail) { Failure = failure; } } public sealed class ConfigPresetRow { public ConfigEntryBase Entry { get; } public string Before { get; } public string After { get; } internal object Value { get; } internal object Previous { get; } internal ConfigPresetRow(ConfigEntryBase entry, object value) { Entry = entry; Value = value; Previous = entry.BoxedValue; Before = TomlTypeConverter.ConvertToString(Previous, entry.SettingType); After = TomlTypeConverter.ConvertToString(value, entry.SettingType); } } public sealed class ConfigPresetResult { public bool Saved { get; } public bool Verified { get; } public bool Synchronized { get; } public Exception? CompletionError { get; } internal ConfigPresetResult(bool saved, bool verified, bool synchronized, Exception? error = null) { Saved = saved; Verified = verified; Synchronized = synchronized; CompletionError = error; } } public sealed class ConfigPresetPreview { [ThreadStatic] private static bool applying; private readonly ConfigFile file; private readonly ConfigPresetRow[] rows; private readonly long generation; private readonly SessionRole role; private readonly int thread; private bool consumed; public string ProductId { get; } public ConfigScope Scope { get; } public IReadOnlyList<ConfigPresetRow> Rows { get; } private ConfigPresetPreview(string product, ConfigScope scope, ConfigFile file, ConfigPresetRow[] rows) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) ProductId = product; Scope = scope; this.file = file; this.rows = rows; Rows = Array.AsReadOnly(rows); generation = Plugin.Session.Generation; role = Plugin.Session.Role; thread = Thread.CurrentThread.ManagedThreadId; } public static ConfigPresetPreview Capture(string productId, ConfigScope scope, ConfigFile file, IReadOnlyDictionary<ConfigDefinition, object> values) { //IL_002e: 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_0033: Invalid comparison between Unknown and I4 //IL_00c4: Unknown result type (might be due to invalid IL or missing references) if (applying || !Plugin.IsReady || file == null || string.IsNullOrWhiteSpace(productId) || values == null || values.Count == 0 || values.Count > 32 || ((int)scope != 0 && (int)scope != 1)) { throw Fault(ConfigPresetFailure.Invalid, "A preset needs one active owner, one scope and 1–32 exact values."); } List<ConfigPresetRow> list = new List<ConfigPresetRow>(); foreach (KeyValuePair<ConfigDefinition, object> value in values) { if (value.Key == (ConfigDefinition)null || !file.ContainsKey(value.Key)) { throw Fault(ConfigPresetFailure.Invalid, "Unknown preset key."); } ConfigEntryBase entry = file[value.Key]; ValidateValue(entry, value.Value); list.Add(new ConfigPresetRow(entry, value.Value)); } ConfigPresetPreview configPresetPreview = new ConfigPresetPreview(productId, scope, file, list.ToArray()); configPresetPreview.Validate(original: true); return configPresetPreview; } private static ConfigPresetException Fault(ConfigPresetFailure failure, string detail) { return new ConfigPresetException(failure, detail); } private static void ValidateValue(ConfigEntryBase entry, object value) { Type settingType = entry.SettingType; if (!(((object)entry).GetType() != typeof(ConfigEntry<>).MakeGenericType(settingType)) && value != null && !(value.GetType() != settingType) && (settingType.IsEnum || settingType == typeof(bool) || settingType == typeof(int) || settingType == typeof(float) || settingType == typeof(double) || settingType == typeof(string)) && (!settingType.IsEnum || Enum.IsDefined(settingType, value)) && (!(value is float f) || (!float.IsNaN(f) && !float.IsInfinity(f))) && (!(value is double d) || (!double.IsNaN(d) && !double.IsInfinity(d)))) { AcceptableValueBase acceptableValues = entry.Description.AcceptableValues; if (acceptableValues == null || acceptableValues.IsValid(value)) { object objB = TomlTypeConverter.ConvertToValue(TomlTypeConverter.ConvertToString(value, settingType), settingType); if (!object.Equals(value, objB)) { throw Fault(ConfigPresetFailure.Invalid, "Preset value does not round-trip through the configuration serializer."); } return; } } throw Fault(ConfigPresetFailure.Invalid, "Preset value has the wrong type or violates the current constraint."); } private void Validate(bool original) { //IL_0038: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Invalid comparison between Unknown and I4 //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) if (consumed || Thread.CurrentThread.ManagedThreadId != thread || !Plugin.IsReady || Plugin.Session.Generation != generation || Plugin.Session.Role != role) { throw Fault(ConfigPresetFailure.Stale, "Preset preview no longer belongs to this session."); } if ((int)Scope == 1 && (!Plugin.Session.IsAuthority || !Plugin.Session.AuthorityReady)) { throw Fault(ConfigPresetFailure.HostOnly, "Host presets must execute on their authoritative host."); } ConfigPresetRow[] array = rows; foreach (ConfigPresetRow configPresetRow in array) { ConfigEntryBase entry = configPresetRow.Entry; if (entry.ConfigFile != file || !file.ContainsKey(entry.Definition) || file[entry.Definition] != entry) { throw Fault(ConfigPresetFailure.Stale, "A previewed entry was replaced."); } if (!MeadworksConfig.TryGetMetadata(entry, out ConfigEntryMetadata metadata) || metadata.ProductId != ProductId || metadata.Scope != Scope || (int)metadata.Visibility != 0) { throw Fault(ConfigPresetFailure.Invalid, "Preset entry owner, visibility or scope changed."); } AuthorityDecision decision = metadata.Decision; if (!decision.Allowed || decision.RequiresServerAmendment || (int)decision.Source != 0) { throw Fault(ConfigPresetFailure.Locked, "Preset entry is locked or carries a server overlay."); } ValidateValue(entry, configPresetRow.Value); if (TomlTypeConverter.ConvertToString(entry.BoxedValue, entry.SettingType) != (original ? configPresetRow.Before : configPresetRow.After)) { throw Fault(ConfigPresetFailure.Stale, "A previewed value changed."); } } } public ConfigPresetResult Apply() { if (applying) { throw Fault(ConfigPresetFailure.Invalid, "A preset operation is already applying."); } Validate(original: true); bool saved = false; bool verified = false; bool autoSave = file.SaveOnConfigSet; applying = true; try { if (!MeadworksConfig.AmendBatch(ProductId, rows.Select((ConfigPresetRow row) => row.Entry).ToArray(), delegate { Validate(original: true); file.SaveOnConfigSet = false; try { ConfigPresetRow[] array = rows; foreach (ConfigPresetRow configPresetRow in array) { configPresetRow.Entry.BoxedValue = configPresetRow.Value; } Validate(original: false); MeadworksConfig.PersistPresetEntries(rows.Select((ConfigPresetRow row) => row.Entry).ToArray()); saved = true; verified = true; } catch (PresetPublicationException) { saved = true; throw; } catch (Exception item) { List<Exception> list = new List<Exception> { item }; for (int num = rows.Length - 1; num >= 0; num--) { try { rows[num].Entry.BoxedValue = rows[num].Previous; } catch (Exception item2) { list.Add(item2); } } if (list.Count > 1) { throw new AggregateException("Preset failed and one or more RAM values could not be restored.", list); } throw; } finally { file.SaveOnConfigSet = autoSave; } }).Applied) { throw Fault(ConfigPresetFailure.Locked, "Preset batch was refused before its write."); } return new ConfigPresetResult(saved, verified, synchronized: true); } catch (Exception error) when (saved) { return new ConfigPresetResult(saved: true, verified, synchronized: false, error); } finally { applying = false; consumed = true; file.SaveOnConfigSet = autoSave; } } } internal static class ConfigValueReadback { private static readonly char[] invalidNameChars = new char[8] { '=', '\n', '\t', '\\', '"', '\'', '[', ']' }; internal static bool TryReload(ConfigEntryBase entry, string path) { List<string> list = new List<string>(); string text = ""; using (StreamReader streamReader = new StreamReader(path, Encoding.UTF8, detectEncodingFromByteOrderMarks: true)) { string text2; while ((text2 = streamReader.ReadLine()) != null) { string text3 = text2.Trim(); if (text3.StartsWith("#")) { continue; } if (text3.StartsWith("[") && text3.EndsWith("]")) { text = text3.Substring(1, text3.Length - 2); continue; } int num = text3.IndexOf('='); if (num >= 0) { string text4 = text3.Substring(0, num).Trim(); if (!ValidName(text) || !ValidName(text4)) { return false; } if (text == entry.Definition.Section && text4 == entry.Definition.Key) { list.Add(text3.Substring(num + 1).Trim()); } } } } if (list.Count == 0) { return false; } foreach (string item in list) { entry.SetSerializedValue(item); } return true; } private static bool ValidName(string name) { if (name == name.Trim()) { return name.IndexOfAny(invalidNameChars) < 0; } return false; } } public sealed class DiagnosticsCommand : ConsoleCommand { public override string Name => "meadworks"; public override string Help => "Meadworks diagnostics (read-only); 'meadworks log <level> [product]' and 'meadworks log filter ...' set logging."; public override void Run(string[] args) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Unknown result type (might be due to invalid IL or missing references) if (args != null && args.Length != 0 && string.Equals(args[0], "log", StringComparison.OrdinalIgnoreCase)) { foreach (string item in MeadworksLogging.Command(args.Skip(1).ToArray())) { Console instance = Console.instance; if (instance != null) { instance.Print(item); } } return; } if (!Plugin.IsReady) { Console instance2 = Console.instance; if (instance2 != null) { instance2.Print("Crucible is unavailable."); } return; } DiagnosticSnapshot val = Plugin.Services.Diagnostics(); SessionSnapshot session = Plugin.Session; ServerPresenceTimer serverPresence = Plugin.ServerPresence; Console instance3 = Console.instance; if (instance3 != null) { instance3.Print(DiagnosticsText.Header(val, PeerProtocol.Version.ToString()) + " | plugin 0.3.34"); } Console instance4 = Console.instance; if (instance4 != null) { instance4.Print(DiagnosticsText.Authority(session.Role, session.AuthorityReady, session.IsAdmin, MeadworksAuthority.Server, serverPresence.Waiting, serverPresence.TimeoutSeconds) + " | peer-class handshake " + (Plugin.PeerClassHandshake ? "on" : "off")); } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { AdmissionDecision val2 = MeadworksAdmission.Current(); Console instance5 = Console.instance; if (instance5 != null) { instance5.Print(val2.Summary + " | peers: " + MeadworksAdmission.Peers.Describe()); } } Console instance6 = Console.instance; if (instance6 != null) { instance6.Print(((object)val.Requests).ToString()); } Console instance7 = Console.instance; if (instance7 != null) { instance7.Print(MeadworksMischief.Diagnostics()); } Func<IReadOnlyList<string>> func = default(Func<IReadOnlyList<string>>); foreach (ProductMetadata product in val.Products) { Console instance8 = Console.instance; if (instance8 != null) { instance8.Print(DiagnosticsText.Product(product, MeadworksAuthority.Facts(product.Id))); } StepAsideDecision val3 = MeadworksStepAside.Decide(product.Id); if (val3.Yield) { Console instance9 = Console.instance; if (instance9 != null) { instance9.Print(" steps aside: " + string.Join(", ", val3.Present.Select((SourceMod s) => s.Name))); } } foreach (ModuleStepAside item2 in Plugin.Services.StepAside.Modules(product.Id)) { Console instance10 = Console.instance; if (instance10 != null) { instance10.Print(" module " + item2.ModuleId + " steps aside: " + item2.Source.Name); } } if (!Plugin.Services.Capabilities.TryResolve<Func<IReadOnlyList<string>>>("meadworks.diagnostics." + product.Id, new Version(1, 0), ref func)) { continue; } try { foreach (string item3 in func().Take(64)) { Console instance11 = Console.instance; if (instance11 != null) { instance11.Print(" " + item3); } } } catch (Exception ex) { Console instance12 = Console.instance; if (instance12 != null) { instance12.Print(" product diagnostics unavailable: " + ex.GetType().Name); } } } Console instance13 = Console.instance; if (instance13 != null) { instance13.Print("log level " + ((object)MeadworksLogging.Policy.Level/*cast due to .constrained prefix*/).ToString() + ((MeadworksLogging.Policy.Overrides.Count > 0) ? (" | overrides " + MeadworksLogging.Policy.FormatOverrides()) : "") + " | console filter " + (MeadworksLogging.ConsoleFilter.Enabled ? "on" : "off")); } foreach (string item4 in MeadworksBuildHashes.Describe()) { Console instance14 = Console.instance; if (instance14 != null) { instance14.Print(item4); } } foreach (CapabilityMetadata capability in val.Capabilities) { Console instance15 = Console.instance; if (instance15 != null) { instance15.Print(capability.Id + " " + capability.Version?.ToString() + " | " + capability.Owner); } } foreach (PluginInfo item5 in Chainloader.PluginInfos.Values.OrderBy<PluginInfo, string>((PluginInfo p) => p.Metadata.GUID, StringComparer.Ordinal)) { Console instance16 = Console.instance; if (instance16 != null) { instance16.Print("loaded: " + item5.Metadata.GUID + " " + item5.Metadata.Version); } } } } public sealed class GuardedOperation : IDisposable { private readonly int before; private int incoming; public GuardScope Scope { get; } internal GuardedOperation(GuardScope scope) { Scope = scope; before = MeadworksAchievementGuard.LocalMarkedCount(); } public void ReportConsumed(ItemData? item) { if (item != null) { Scope.ReportConsumed(item.m_cheated); } } public void ReportConsumed(bool marked) { Scope.ReportConsumed(marked); } public void ReportIncomingMarked(int count = 1) { if (count > 0) { incoming += count; } } public void Dispose() { //IL_0025: 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_003d: Invalid comparison between Unknown and I4 if (!Scope.Disposed) { Scope.Dispose(); int num = MeadworksAchievementGuard.LocalMarkedCount(); if ((int)CheatMarks.Audit(Scope.Action, before, num, incoming) == 1) { MeadworksAchievementGuard.Log?.Warning("Achievement guard: " + ((object)Scope)?.ToString() + " raised the marked item count from " + before + " to " + num + "; Meadworks QoL must not mark items as cheated."); } } } } public static class MeadworksAchievementGuard { public const string AchievementSyncGuid = "liveriadevs.valheim.achievementsync"; internal static MeadworksLog? Log; private static Harmony? harmony; private static ConfigEntry<bool>? syncPastStats; private static ConfigEntry<KeyboardShortcut>? resyncKey; private static readonly Stack<bool> adding = new Stack<bool>(); public static GuardScopes Scopes { get; } = new GuardScopes(); public static Func<bool>? CheatsForgiven { get; set; } public static GuardedOperation Begin(string productId, GuardAction action) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return new GuardedOperation(Scopes.Begin(productId, action)); } public static void Run(string productId, GuardAction action, Action body) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (body == null) { throw new ArgumentNullException("body"); } using (Begin(productId, action)) { body(); } } public static bool IsMarked(ItemData? item) { return item?.m_cheated ?? false; } public static bool CanStack(GuardAction action, ItemData target, ItemData incoming) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return CheatMarks.CanStack(action, IsMarked(target), IsMarked(incoming)); } public static void CopyMark(ItemData from, ItemData to) { if (from == null || to == null) { throw new ArgumentNullException((from == null) ? "from" : "to"); } to.m_cheated = CheatMarks.Split(from.m_cheated); } public static void MarkCheatOutput(ItemData item) { if (item == null) { throw new ArgumentNullException("item"); } item.m_cheated = CheatMarks.Result((GuardAction)8, item.m_cheated); } internal static int LocalMarkedCount() { Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val != null) { return val.GetAllItems().Count((ItemData i) => i?.m_cheated ?? false); } return 0; } internal static void Install(ConfigFile config, ManualLogSource logger) { //IL_0038: 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_005d: Expected O, but got Unknown Log = MeadworksLog.For(logger, "Crucible"); syncPastStats = MeadworksConfig.Bind(config, "Achievements", "SyncPastStats", defaultValue: true, "Copy stats the game counted while achievements were not tracked into the achievement table when a clean character loads (never for characters that used cheats unless Hex allows it). Off while AchievementSync is installed.", (ConfigScope)0); resyncKey = MeadworksConfig.Bind<KeyboardShortcut>(config, "Achievements", "ResyncKey", KeyboardShortcut.Empty, "Key that rechecks your saved game statistics and grants any MeadWorks achievements already earned. Unassigned by default.", (ConfigScope)0); harmony = new Harmony("com.meadworks.crucible.achievements"); Patch(AccessTools.Method(typeof(Inventory), "FindFreeStackItem", new Type[3] { typeof(string), typeof(int), typeof(float) }, (Type[])null), null, "FindFreeStackPostfix"); Type[][] array = new Type[2][] { new Type[1] { typeof(ItemData) }, new Type[2] { typeof(ItemData), typeof(Vector2i) } }; foreach (Type[] array2 in array) { Patch(AccessTools.Method(typeof(Inventory), "AddItem", array2, (Type[])null), "AddPrefix", null, "AddFinalizer"); } Patch(AccessTools.Method(typeof(Inventory), "AddItem", new Type[5] { typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool) }, (Type[])null), "PlacePrefix"); Patch(AccessTools.Method(typeof(Inventory), "ItemCheated", new Type[3] { typeof(Requirement[]), typeof(int), typeof(bool) }, (Type[])null), null, "ItemCheatedPostfix"); Patch(AccessTools.Method(typeof(Player), "OnSpawned", (Type[])null, (Type[])null), null, "SpawnedPostfix"); } private static void Patch(MethodBase? target, string? prefix = null, string? postfix = null, string? finalizer = null) { if (target == null) { Log?.Warning("Achievement guard: a game member was not found; that hook is off."); } else { harmony.Patch(target, M(prefix), M(postfix), (HarmonyMethod)null, M(finalizer), (HarmonyMethod)null); } static HarmonyMethod? M(string? name) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (name != null) { return new HarmonyMethod(typeof(MeadworksAchievementGuard), name, (Type[])null); } return null; } } internal static void Update() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (resyncKey == null || !MeadworksInput.Pressed(resyncKey, (InputContext)0)) { return; } string message; string text = (ResyncStats(out message) ? ("Achievement stats resynced: " + message) : message); Log?.Info(text); if ((int)MeadworksHeadless.Channel == 0) { MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true); } } } internal static void Uninstall() { Harmony? obj = harmony; if (obj != null) { obj.UnpatchSelf(); } harmony = null; Scopes.Reset(); adding.Clear(); } private static void AddPrefix(ItemData item) { adding.Push(item?.m_cheated ?? false); } private static Exception? AddFinalizer(Exception? __exception) { if (adding.Count > 0) { adding.Pop(); } return __exception; } private static void FindFreeStackPostfix(Inventory __instance, string name, int quality, float worldLevel, ref ItemData? __result) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) GuardScope current = Scopes.Current; if (current == null || !CheatMarks.IsQol(current.Action) || adding.Count == 0 || __result == null) { return; } bool mark = adding.Peek(); if (!CheatMarks.CanStack(current.Action, __result.m_cheated, mark)) { __result = ((IEnumerable<ItemData>)__instance.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => i != null && i.m_shared.m_name == name && i.m_quality == quality && i.m_stack < i.m_shared.m_maxStackSize && (float)i.m_worldLevel == worldLevel && i.m_cheated == mark)); } } private static bool PlacePrefix(Inventory __instance, ItemData item, int x, int y, ref bool __result) { //IL_000f: 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) GuardScope current = Scopes.Current; if (current == null || !CheatMarks.IsQol(current.Action) || item == null) { return true; } ItemData itemAt = __instance.GetItemAt(x, y); if (itemAt == null || itemAt == item || !itemAt.IsSameType(item) || CheatMarks.CanStack(current.Action, itemAt.m_cheated, item.m_cheated)) { return true; } __result = false; return false; } private static void ItemCheatedPostfix(ref bool __result) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 GuardScope current = Scopes.Current; if (current != null && (int)current.Action == 4 && current.ConsumedMarked.HasValue) { __result = current.ConsumedMarked.Value; } } private static void SpawnedPostfix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } try { if (ResyncStats(out string message)) { Log?.Info("Achievement stats resynced: " + message); } else { Log?.Verbose("Achievement stat resync skipped: " + message); } } catch (Exception ex) { Log?.Warning("Achievement stat resync failed: " + ex.GetType().Name); } } public static ResyncFacts? Facts() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown Game instance = Game.instance; PlayerProfile val = ((instance != null) ? instance.GetPlayerProfile() : null); if (val == null) { return null; } bool flag = false; try { flag = CheatsForgiven?.Invoke() ?? false; } catch (Exception) { flag = false; } return new ResyncFacts(syncPastStats?.Value ?? false, Chainloader.PluginInfos.ContainsKey("liveriadevs.valheim.achievementsync"), val.m_usedCheats, Achievements.IsWorldCheated(), LocalMarkedCount() > 0, flag); } public static bool ResyncStats(out string message) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_010c: 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_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) Game instance = Game.instance; PlayerProfile val = ((instance != null) ? instance.GetPlayerProfile() : null); ResyncFacts val2 = Facts(); if (val == null || val2 == null) { message = "No character is loaded."; return false; } ResyncRefusal val3 = StatResync.Check(val2); if ((int)val3 != 0) { message = StatResync.Describe(val3); return false; } PlayerStats[] playerStats = val.m_playerStats; if (playerStats == null || playerStats.Length <= 1 || playerStats[0] == null || playerStats[1] == null) { message = "The profile has no stat tables."; return false; } Dictionary<PlayerStatType, float> stats = playerStats[0].m_stats; Dictionary<PlayerStatType, float> stats2 = playerStats[1].m_stats; IReadOnlyList<StatChange> readOnlyList = StatResync.Plan((IReadOnlyDictionary<int, float>)stats.ToDictionary((KeyValuePair<PlayerStatType, float> p) => (int)p.Key, (KeyValuePair<PlayerStatType, float> p) => p.Value), (IReadOnlyDictionary<int, float>)stats2.ToDictionary((KeyValuePair<PlayerStatType, float> p) => (int)p.Key, (KeyValuePair<PlayerStatType, float> p) => p.Value)); foreach (StatChange item in readOnlyList) { StatChange current = item; PlayerStatType val4 = (PlayerStatType)((StatChange)(ref current)).Stat; stats2[val4] = ((StatChange)(ref current)).To; Achievements.AchievementStatSetEvent(val4, StatResync.PlatformValue(((StatChange)(ref current)).To)); } message = readOnlyList.Count + " stat(s) updated."; return true; } } internal static class MeadworksAdmission { internal const double AnswerTimeoutSeconds = 60.0; internal const double FormerAnswerTimeoutSeconds = 30.0; private static ConfigEntry<AdmissionMode>? mode; private static string? lastSummary; internal static PeerPresenceTracker Peers { get; } = new PeerPresenceTracker(60.0, 300.0); private static bool Tracking { get { if (NativePeerJoin.Installed && (Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static void Bind(ConfigFile config) { lastSummary = null; mode = MeadworksConfig.BindHidden<AdmissionMode>(config, "Network", "AdmissionMode", (AdmissionMode)0, "Retained legacy preference. Strict always applies: every client must run Meadworks. AllowVanilla is no longer supported; its saved value is preserved.", (ConfigScope)1); mode.SettingChanged += delegate { try { Report(); } catch (Exception ex) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)("Admission mode check failed: " + ex.GetType().Name)); } } }; } internal static void Register(ProductRegistration crucible) { crucible.AddCapability<Func<int>>("meadworks.admission.vanillasupport", AdmissionPolicy.CapabilityVersion, (Func<int>)(() => 1)); crucible.AddCapability<Func<int>>("meadworks.peers.vanillacount", AdmissionPolicy.CapabilityVersion, (Func<int>)VanillaCount); crucible.AddCapability<Func<long, int>>("meadworks.peers.kind", AdmissionPolicy.CapabilityVersion, (Func<long, int>)KindOf); } private static int VanillaCount() { if (!Tracking) { return -1; } return Peers.NotCrucibleCount(from p in ZNet.instance.GetPeers() where p != null && p.IsReady() select p.m_uid); } private static int KindOf(long peerId) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected I4, but got Unknown if (!Tracking) { return 0; } return (int)Peers.KindOf(peerId); } internal static AdmissionDecision Current() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<AdmissionMode>? obj = mode; return AdmissionPolicy.Decide((AdmissionMode)((obj != null) ? ((int)obj.Value) : 0), (IEnumerable<AdmissionProduct>)Array.Empty<AdmissionProduct>(), (IEnumerable<string>)Array.Empty<string>()); } internal static AdmissionDecision Report() { //IL_0024: 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) AdmissionDecision val = Current(); if (val.Summary != lastSummary) { lastSummary = val.Summary; if (val.Requested != val.Effective) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)val.Summary); } } else { ManualLogSource? log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)val.Summary); } } } return val; } internal static void Reset() { Peers.Reset(); } } public static class MeadworksAuthority { private static readonly Dictionary<string, ServerProduct> serverProducts = new Dictionary<string, ServerProduct>(StringComparer.Ordinal); private static AuthorityMemo? memo; private static AuthorityMemo Memo { get { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0025: Expected O, but got Unknown ProductRegistry services = Plugin.Services; AuthorityMemo val = memo; if (val == null || !val.IsFor(services)) { AuthorityMemo val2 = new AuthorityMemo(services); val = val2; memo = val2; } return val; } } public static ServerCrucible Server { get { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsReady) { return (ServerCrucible)0; } return ServerPresenceTimer.For(Plugin.Session.Role, Plugin.ServerPresence.State, Plugin.Session.AuthorityReady); } } public static PeerClass PeerClassOf(string productId) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsReady) { return (PeerClass)0; } return Memo.PeerClassOf(productId); } public static ServerProduct ServerProductOf(string productId) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!serverProducts.TryGetValue(productId ?? "", out var value)) { return (ServerProduct)0; } return value; } public static AuthorityFacts Facts(string productId) { //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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown if (!Plugin.IsReady) { return new AuthorityFacts((SessionRole)3, false, false, (ServerCrucible)0, (PeerClass)0, (ServerProduct)0); } return Memo.Facts(Plugin.Session, Server, productId, ServerProductOf(productId)); } public static AuthorityDecision Decide(string productId, ConfigScope scope, bool serverLocked = true) { //IL_001f: 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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsReady) { return AuthorityGate.Decide(scope, Facts(productId), serverLocked); } return Memo.Decide(Plugin.Session, Server, productId, ServerProductOf(productId), scope, serverLocked); } public static bool ProductActive(string productId, out ConfigBlockReason reason) { //IL_001e: 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) if (!Plugin.IsReady) { return AuthorityGate.ProductActive(Facts(productId), ref reason); } return Memo.ProductActive(Plugin.Session, Server, productId, ServerProductOf(productId), ref reason); } internal static void SetServerProducts(IEnumerable<KeyValuePair<string, ServerProduct>> states) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) serverProducts.Clear(); foreach (KeyValuePair<string, ServerProduct> state in states) { serverProducts[state.Key] = state.Value; } } internal static void ClearServerProducts() { serverProducts.Clear(); } } public static class MeadworksBlockedOptions { private sealed class Tracked { public readonly string Product; public readonly ConfigEntryBase Entry; public readonly ConfigScope Scope; public readonly bool ServerLocked; public readonly bool ValidNames; public bool Applied; public Tracked(string product, ConfigEntryBase entry, ConfigScope scope, bool serverLocked) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Product = product; Entry = entry; Scope = scope; ServerLocked = serverLocked; ValidNames = BlockedOptionNotices.IsValidName(product) && BlockedOptionNotices.IsValidName(entry.Definition.Key); } } private sealed class ConfigurationManagerAttributes { public bool? ReadOnly = true; } private sealed class ReadOnlyTag { public bool ReadOnly = true; } private static readonly BlockedOptionNotices notices = new BlockedOptionNotices(4096); private static readonly List<Tracked> tracked = new List<Tracked>(); private static readonly FieldInfo? TagsField = AccessTools.Field(typeof(ConfigDescription), "<Tags>k__BackingField"); private static readonly ConfigurationManagerAttributes ManagerMarker = new ConfigurationManagerAttributes(); private static readonly ReadOnlyTag CharterMarker = new ReadOnlyTag(); private static MeadworksLog? log; private static readonly SweepSlicer sweep = new SweepSlicer(0.5f, 32); public static string Language => MeadworksTranslations.GameLanguage(); private static MeadworksLog Log => log ?? (log = MeadworksLog.For(Plugin.Log ?? Logger.CreateLogSource("Meadworks"), "crucible")); public static BlockedOption Query(string productId, string option, ConfigScope scope, bool serverLocked = true) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 AuthorityDecision val = MeadworksAuthority.Decide(productId, scope, serverLocked); if ((int)scope != 2 || !MeadworksMischief.PlayersMay(productId, option)) { return BlockedOption.From(productId, option, val); } return BlockedOption.ForUse(productId, option, val); } public static BlockedOption QueryEdit(string productId, string option, ConfigScope scope, bool serverLocked = true) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) return BlockedOption.From(productId, option, MeadworksAuthority.Decide(productId, scope, serverLocked)); } public static bool TryEdit(string productId, string option, ConfigScope scope, string? label = null, bool serverLocked = true) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) BlockedOption obj = QueryEdit(productId, option, scope, serverLocked); Notify(obj, label); return obj.Allowed; } public static string Reason(ConfigBlockReason reason) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return BlockedOptionText.Reason(reason, Language, (ProductTranslations)null); } public static bool TryUse(string productId, string option, ConfigScope scope, string? label = null, bool serverLocked = true) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) BlockedOption obj = Query(productId, option, scope, serverLocked); Notify(obj, label); return obj.Allowed; } public static void Notify(BlockedOption status, string? label = null) { //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_0072: Unknown result type (might be due to invalid IL or missing references) if (status == null) { throw new ArgumentNullException("status"); } long num = (Plugin.IsReady ? Plugin.Session.Generation : 0); if ((int)notices.Observe(num, status.Product, status.Option, status.Reason) == 0) { string text = status.Message(string.IsNullOrWhiteSpace(label) ? status.Option : label, Language, (ProductTranslations)null); Log.Info(((object)status).ToString()); if ((int)MeadworksHeadless.Channel == 0) { MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true); } else { Log.Info(text); } } } public static void Track(string productId, ConfigEntryBase entry, ConfigScope scope, bool serverLocked = true) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (entry == null) { throw new ArgumentNullException("entry"); } MeadworksConfig.RecordTracking(entry, productId, scope, serverLocked); Tracked item = new Tracked(productId, entry, scope, serverLocked); tracked.Add(item); Refresh(item); } public static void Untrack(ConfigEntryBase entry) { for (int num = tracked.Count - 1; num >= 0; num--) { if (tracked[num].Entry == entry) { Apply(tracked[num], readOnly: false); tracked.RemoveAt(num); sweep.Removed(num); } } MeadworksConfig.EndTracking(entry); } internal static void Update(float now, float deltaTime) { int num2 = default(int); int num = sweep.Take(now, deltaTime, tracked.Count, ref num2); for (int i = num2; i < num2 + num; i++) { Refresh(tracked[i]); } } internal static void Shutdown() { foreach (Tracked item in tracked) { Apply(item, readOnly: false); MeadworksConfig.EndTracking(item.Entry); } tracked.Clear(); notices.Reset(); } private static void Refresh(Tracked item) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) bool flag; try { bool allowed = MeadworksAuthority.Decide(item.Product, item.Scope, item.ServerLocked).Allowed; flag = item.ValidNames && !allowed; } catch (ArgumentException) { flag = false; } if (ConfigTags.Changed(item.Applied, flag)) { Apply(item, flag); } } private static void Apply(Tracked item, bool readOnly) { ConfigDescription description = item.Entry.Description; item.Applied = readOnly; if (!(TagsField == null) && description != null && description != ConfigDescription.Empty) { object[] array = ConfigTags.Apply(description.Tags, (object)CharterMarker, readOnly); TagsField.SetValue(description, ConfigTags.Apply(array, (object)ManagerMarker, readOnly)); } } } internal static class MeadworksBuildHashes { internal const string Capability = "meadworks.crucible.build_hashes"; internal static readonly Version CapabilityVersion = new Version(1, 0); private static IReadOnlyList<BuildEntry>? cached; internal static void Register(ProductRegistration crucible) { crucible.AddCapability<Func<IReadOnlyList<BuildEntry>>>("meadworks.crucible.build_hashes", CapabilityVersion, (Func<IReadOnlyList<BuildEntry>>)Local); } internal static IReadOnlyList<BuildEntry> Local() { //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Expected O, but got Unknown if (cached != null) { return cached; } List<BuildEntry> list = new List<BuildEntry>(); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); foreach (Assembly item in (from a in AppDomain.CurrentDomain.GetAssemblies() where a.GetName().Name.StartsWith("Crucible", StringComparison.Ordinal) select a).Concat(from p in Chainloader.PluginInfos.Values where p.Metadata.GUID.StartsWith("com.meadworks.", StringComparison.Ordinal) && (Object)(object)p.Instance != (Object)null select ((object)p.Instance).GetType().Assembly)) { string name = item.GetName().Name; if (!hashSet.Add(name)) { continue; } try { if (item.IsDynamic || string.IsNullOrEmpty(item.Location) || !File.Exists(item.Location)) { continue; } using SHA256 sHA = SHA256.Create(); using FileStream inputStream = File.OpenRead(item.Location); list.Add(new BuildEntry(name, item.GetName().Version?.ToString() ?? "", BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", ""))); } catch (Exception ex) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)("Build hash of " + name + " unavailable: " + ex.GetType().Name)); } } } return cached = list.OrderBy<BuildEntry, string>((BuildEntry e) => e.Assembly, StringComparer.Ordinal).ToList(); } internal static IEnumerable<string> Describe() { return from e in Local() select "build: " + e.Assembly + " " + e.Version + " " + e.Sha256.Substring(0, 12); } } public sealed class ConfigClauseRegistration { public string ProductId { get; } public ConfigScope Scope { get; } public object OwnValue { get; } public bool IsBound { get; } public bool MayAmend { get; } internal ConfigClauseRegistration(string product, bool local, object own, bool bound, bool mayAmend) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) ProductId = product; Scope = (ConfigScope)(!local); OwnValue = own; IsBound = bound; MayAmend = mayAmend; } } public sealed class ConfigAmendResult { public AmendRoute Route { get; } public AuthorityDecision Decision { get; } public bool Applied { get; } internal ConfigAmendResult(AmendRoute route, AuthorityDecision decision, bool applied) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) Route = route; Decision = decision; Applied = applied; } public override string ToString() { //IL_0009: 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) return ((object)Route/*cast due to .constrained prefix*/).ToString() + (Applied ? " applied" : " not applied") + " (" + ((object)Decision)?.ToString() + ")"; } } internal static class JotunnConfigBridge { private static readonly MethodInfo? Cache = AccessTools.Method(typeof(SynchronizationManager), "CacheConfigurationValues", (Type[])null, (Type[])null); private static readonly MethodInfo? Send = AccessTools.Method(typeof(SynchronizationManager), "SynchronizeChangedConfig", (Type[])null, (Type[])null); internal static bool Available { get { if (Cache != null) { return Send != null; } return false; } } internal static bool Publish(Action apply) { if (!Available) { apply(); ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Meadworks:Crucible] Jotunn configuration synchronization is unavailable; the change stays on this machine."); } return false; } Cache.Invoke(SynchronizationManager.Instance, null); SyncedConfigGuard.AsServer(apply); Send.Invoke(SynchronizationManager.Instance, null); return true; } } public sealed class ConfigEntryMetadata { public string ProductId { get; } public ConfigScope Scope { get; } public object? SafeValue { get; } public bool ServerLocked { get; } public ConfigVisibility Visibility { get; } public AuthorityDecision Decision => MeadworksAuthority.Decide(ProductId, Scope, ServerLocked); internal ConfigEntryMetadata(string product, ConfigScope scope, object? safe, bool locked, ConfigVisibility visibility = (ConfigVisibility)0) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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) ProductId = product; Scope = scope; SafeValue = safe; ServerLocked = locked; Visibility = visibility; } } public sealed class ConfigPresentationText { public string Label { get; } public string Section { get; } public string Help { get; } public ConfigPresentationText(string? label = null, string? section = null, string? help = null) { Label = label ?? ""; Section = section ?? ""; Help = help ?? ""; } } internal interface IConfigPresentation { string ProductId { get; } bool Supports(ConfigEntryBase entry); ConfigPresentationText? Resolve(ConfigEntryBase entry, string language); } public sealed class ConfigPresentation<T> : IConfigPresentation { private readonly string productId; private readonly Func<ConfigEntry<T>, string, ConfigPresentationText?> resolve; string IConfigPresentation.ProductId => productId; public ConfigPresentation(string productId, Func<ConfigEntry<T>, string, ConfigPresentationText?> resolve) { if (string.IsNullOrWhiteSpace(productId)) { throw new ArgumentException("A registered product owner is required.", "productId"); } this.productId = productId; this.resolve = resolve ?? throw new ArgumentNullException("resolve"); } bool IConfigPresentation.Supports(ConfigEntryBase entry) { return ((object)entry).GetType() == typeof(ConfigEntry<T>); } ConfigPresentationText? IConfigPresentation.Resolve(ConfigEntryBase entry, string language) { return resolve((ConfigEntry<T>)(object)entry, language); } } internal static class SettingTranslations { private static readonly ConcurrentDictionary<string, ProductTranslations> byPlugin = new ConcurrentDictionary<string, ProductTranslations>(StringComparer.Ordinal); internal static void Add(string pluginGuid, ProductTranslations translations) { if (!string.IsNullOrEmpty(pluginGuid) && translations != null) { byPlugin.TryAdd(pluginGuid, translations); } } internal static void Remove(string pluginGuid, ProductTranslations translations) { if (pluginGuid != null && byPlugin.TryGetValue(pluginGuid, out ProductTranslations value) && value == translations) { byPlugin.TryRemove(pluginGuid, out ProductTranslations _); } } internal static bool TryGet(string pluginGuid, out ProductTranslations translations) { return byPlugin.TryGetValue(pluginGuid ?? "", out translations); } } internal static class SyncedConfigGuard { private static readonly SyncedValueLedger Ledger = new SyncedValueLedger(); private static readonly HashSet<ConfigFile> Files = new HashSet<ConfigFile>(); private static readonly HashSet<ConfigEntryBase> Reported = new HashSet<ConfigEntryBase>(); private static Harmony? harmony; private static bool active; private static int applying; private static bool restoring; internal static void Install() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_0085: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected O, but got Unknown if (harmony != null) { return; } harmony = new Harmony("com.meadworks.crucible.synced-config"); MethodInfo methodInfo = AccessTools.Method(typeof(SynchronizationManager), "ApplyConfigZPackage", (Type[])null, (Type[])null); if (methodInfo == null) { ManualLogSource? log = Plugin.Log; if (log != null) { log.LogWarning((object)"[Meadworks:Crucible] Jotunn's configuration apply step was not found; server values on clients are not re-applied after local changes."); } return; } try { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(SyncedConfigGuard), "ApplyPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(SyncedConfigGuard), "ApplyFinalizer", (Type[])null), (HarmonyMethod)null); HarmonyMethod val = new HarmonyMethod(typeof(SyncedConfigGuard), "ForgetPrefix", (Type[])null) { priority = 800 }; string[] array = new string[2] { "Shutdown", "OnDestroy" }; foreach (string text in array) { MethodInfo methodInfo2 = AccessTools.Method(typeof(ZNet), text, (Type[])null, (Type[])null); if (methodInfo2 != null) { harmony.Patch((MethodBase)methodInfo2, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } SynchronizationManager.OnConfigurationSynchronized += OnSynchronized; active = true; } catch (Exception ex) { harmony.UnpatchSelf(); active = false; ManualLogSource? log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[Meadworks:Crucible] Server-value guard is off: " + ex.GetType().Name + ": " + ex.Message)); } } } internal static void Uninstall() { SynchronizationManager.OnConfigurationSynchronized -= OnSynchronized; foreach (ConfigFile file in Files) { file.SettingChanged -= OnChanged; } Files.Clear(); Forget(); active = false; Harmony? obj = harmony; if (obj != null) { obj.UnpatchSelf(); } harmony = null; } internal static void Watch(ConfigEntryBase entry) { ConfigFile val = ((entry != null) ? entry.ConfigFile : null); if (val != null &&