using 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 GetAvailableAsync(AsyncResourceContext context, string itemId, CancellationToken cancellation); Task TryReserveAsync(AsyncResourceContext context, IReadOnlyList costs, CancellationToken cancellation); } public interface IAsyncResourceReservation : IDisposable { AsyncResourceContext Identity { get; } AsyncReservationState State { get; } Task CommitAsync(CancellationToken cancellation); Task RollbackAsync(CancellationToken cancellation); Task 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>); public static Tuple 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? 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>? provider, out EmberSleepState state) { state = EmberSleepState.Closed; if (provider == null) { return false; } Tuple raw; try { raw = provider(); } catch { return false; } return TryRead(raw, out state); } } public readonly struct EmberSleepState : IEquatable { 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); 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? 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? 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); 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? 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); 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? 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>); public static bool TryRead(Func>? provider, out IReadOnlyCollection names) { names = (IReadOnlyCollection)(object)Array.Empty(); if (provider == null) { return false; } try { IReadOnlyCollection readOnlyCollection = provider(); if (readOnlyCollection == null) { return false; } names = readOnlyCollection; return true; } catch { return false; } } public static bool Contains(IReadOnlyCollection 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>); public static Tuple 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>? provider, out InventoryLayout layout) { layout = InventoryLayout.None; if (provider == null) { return false; } Tuple 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 { 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>>); public static bool TryRead(Func>>? provider, out int stack, out IReadOnlyCollection items) { stack = 0; items = (IReadOnlyCollection)(object)Array.Empty(); if (provider == null) { return false; } Tuple> 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>); 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>? 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 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>); public static bool TryMake(Func>? provider, object column, out Tuple? 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); public static bool TryGet(IReadOnlyDictionary? 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? api) { if (!TryGet>(api, "GetApiVersion", out Func 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>); } 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, Func[]>>); } 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); public static bool Exempt(Func? 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); public static bool TryRun(Func? 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, Tuple>>); public static bool TryRun(Func, Tuple>>? provider, string operation, IReadOnlyList arguments, out IReadOnlyList lines) { lines = Array.Empty(); if (provider == null || string.IsNullOrEmpty(operation) || arguments == null) { return false; } Tuple> 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 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>); public static Tuple Pack(SeasonState state) { return Tuple.Create((int)state.Season, state.DayInSeason, state.DaysInSeason, state.DayOfYear, state.YearLength, state.Revision); } public static bool TryRead(Tuple? 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>? provider, out SeasonState state) { state = SeasonState.Unavailable; if (provider == null) { return false; } Tuple raw; try { raw = provider(); } catch { return false; } return TryRead(raw, out state); } public static SeasonState ReadOrUnavailable(Func>? provider) { if (!TryRead(provider, out var state)) { return SeasonState.Unavailable; } return state; } } public readonly struct SeasonState : IEquatable { 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); public static bool Valid(int support) { if (support >= 0) { return support <= 2; } return false; } public static bool TryRead(Func? 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); public static bool TryRead(Func? 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? 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); public static int Read(Func? 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>); public static Tuple 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? 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>? provider, out WeatherEventState state) { state = WeatherEventState.Idle; if (provider == null) { return false; } Tuple 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>); public static bool TryRead(Tuple? 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>? provider, out WeatherEventDetail detail) { detail = WeatherEventDetail.Idle; if (provider == null) { return false; } Tuple 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 { 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; } } }